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,1059 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import Counter, defaultdict
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from time import perf_counter
|
|
6
|
+
from typing import (
|
|
7
|
+
Any,
|
|
8
|
+
Callable,
|
|
9
|
+
Dict,
|
|
10
|
+
Hashable,
|
|
11
|
+
Iterable,
|
|
12
|
+
List,
|
|
13
|
+
Mapping,
|
|
14
|
+
Optional,
|
|
15
|
+
Sequence,
|
|
16
|
+
Set,
|
|
17
|
+
Tuple,
|
|
18
|
+
)
|
|
19
|
+
import hashlib
|
|
20
|
+
|
|
21
|
+
import networkx as nx
|
|
22
|
+
|
|
23
|
+
from ..kinds import REACTION_KINDS
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"SymmetryConfig",
|
|
27
|
+
"WLResult",
|
|
28
|
+
"AutomorphismResult",
|
|
29
|
+
"IsomorphismResult",
|
|
30
|
+
"CanonicalResult",
|
|
31
|
+
"freeze",
|
|
32
|
+
"hash_text",
|
|
33
|
+
"prepare_graph",
|
|
34
|
+
"node_token",
|
|
35
|
+
"edge_token",
|
|
36
|
+
"node_matcher",
|
|
37
|
+
"edge_matcher",
|
|
38
|
+
"build_fast_signature",
|
|
39
|
+
"invert_mapping",
|
|
40
|
+
"orbits_from_mappings",
|
|
41
|
+
"should_stop",
|
|
42
|
+
"graph_key_from_order",
|
|
43
|
+
"approx_automorphism_count_from_cells",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class SymmetryConfig:
|
|
49
|
+
"""Configuration controlling semantic versus topological symmetry matching.
|
|
50
|
+
|
|
51
|
+
The configuration determines which node and edge attributes participate in
|
|
52
|
+
token construction, canonicalization, isomorphism tests, and automorphism
|
|
53
|
+
analysis.
|
|
54
|
+
|
|
55
|
+
:param kind_attr_key:
|
|
56
|
+
Node attribute name used to identify node kind.
|
|
57
|
+
:type kind_attr_key: str
|
|
58
|
+
|
|
59
|
+
:param species_kind_value:
|
|
60
|
+
Attribute value indicating a species node.
|
|
61
|
+
:type species_kind_value: str
|
|
62
|
+
|
|
63
|
+
:param rule_kind_value:
|
|
64
|
+
Attribute value indicating a rule/reaction node.
|
|
65
|
+
:type rule_kind_value: str
|
|
66
|
+
|
|
67
|
+
:param require_kind:
|
|
68
|
+
Whether node kind must always be included in node tokens.
|
|
69
|
+
:type require_kind: bool
|
|
70
|
+
|
|
71
|
+
:param species_identity_keys:
|
|
72
|
+
Preferred attribute keys used to identify species nodes.
|
|
73
|
+
:type species_identity_keys: Tuple[str, ...]
|
|
74
|
+
|
|
75
|
+
:param rule_identity_keys:
|
|
76
|
+
Preferred attribute keys used to identify rule nodes.
|
|
77
|
+
:type rule_identity_keys: Tuple[str, ...]
|
|
78
|
+
|
|
79
|
+
:param species_extra_attr_keys:
|
|
80
|
+
Additional species-node attributes to include in node tokens.
|
|
81
|
+
:type species_extra_attr_keys: Tuple[str, ...]
|
|
82
|
+
|
|
83
|
+
:param rule_extra_attr_keys:
|
|
84
|
+
Additional rule-node attributes to include in node tokens.
|
|
85
|
+
:type rule_extra_attr_keys: Tuple[str, ...]
|
|
86
|
+
|
|
87
|
+
:param other_node_attr_keys:
|
|
88
|
+
Attributes to include for nodes that are neither species nor rule nodes.
|
|
89
|
+
:type other_node_attr_keys: Tuple[str, ...]
|
|
90
|
+
|
|
91
|
+
:param generic_node_attr_keys:
|
|
92
|
+
Generic node attributes appended for all node kinds.
|
|
93
|
+
:type generic_node_attr_keys: Tuple[str, ...]
|
|
94
|
+
|
|
95
|
+
:param ignored_node_attr_keys:
|
|
96
|
+
Node attributes ignored during token construction.
|
|
97
|
+
:type ignored_node_attr_keys: Tuple[str, ...]
|
|
98
|
+
|
|
99
|
+
:param edge_attr_keys:
|
|
100
|
+
Edge attributes included in edge tokens.
|
|
101
|
+
:type edge_attr_keys: Tuple[str, ...]
|
|
102
|
+
|
|
103
|
+
:param ignored_edge_attr_keys:
|
|
104
|
+
Edge attributes ignored during edge token construction.
|
|
105
|
+
:type ignored_edge_attr_keys: Tuple[str, ...]
|
|
106
|
+
|
|
107
|
+
:param use_edge_direction:
|
|
108
|
+
Whether edge direction is respected in symmetry computations.
|
|
109
|
+
:type use_edge_direction: bool
|
|
110
|
+
|
|
111
|
+
:param include_missing_keys:
|
|
112
|
+
Whether missing identity keys should be represented explicitly as
|
|
113
|
+
``None`` in node tokens.
|
|
114
|
+
:type include_missing_keys: bool
|
|
115
|
+
|
|
116
|
+
.. rubric:: Examples
|
|
117
|
+
|
|
118
|
+
.. code-block:: python
|
|
119
|
+
|
|
120
|
+
from synkit.CRN.Sym._common import SymmetryConfig
|
|
121
|
+
|
|
122
|
+
semantic_cfg = SymmetryConfig.semantic()
|
|
123
|
+
topo_cfg = SymmetryConfig.topological()
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
kind_attr_key: str = "kind"
|
|
127
|
+
species_kind_value: str = "species"
|
|
128
|
+
rule_kind_value: str = "rule"
|
|
129
|
+
require_kind: bool = True
|
|
130
|
+
|
|
131
|
+
species_identity_keys: Tuple[str, ...] = ("smiles", "label")
|
|
132
|
+
rule_identity_keys: Tuple[str, ...] = ("rule_repr", "rxn_repr", "label")
|
|
133
|
+
|
|
134
|
+
species_extra_attr_keys: Tuple[str, ...] = ()
|
|
135
|
+
rule_extra_attr_keys: Tuple[str, ...] = ()
|
|
136
|
+
other_node_attr_keys: Tuple[str, ...] = ()
|
|
137
|
+
generic_node_attr_keys: Tuple[str, ...] = ()
|
|
138
|
+
|
|
139
|
+
ignored_node_attr_keys: Tuple[str, ...] = (
|
|
140
|
+
"syncrn_id",
|
|
141
|
+
"source_node_id",
|
|
142
|
+
"app_index",
|
|
143
|
+
"rule_index",
|
|
144
|
+
"step",
|
|
145
|
+
"rxn_id",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
edge_attr_keys: Tuple[str, ...] = ("role", "stoich")
|
|
149
|
+
ignored_edge_attr_keys: Tuple[str, ...] = ("rxn_id",)
|
|
150
|
+
use_edge_direction: bool = True
|
|
151
|
+
include_missing_keys: bool = False
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def semantic(cls) -> "SymmetryConfig":
|
|
155
|
+
"""Build a semantic symmetry configuration.
|
|
156
|
+
|
|
157
|
+
:return:
|
|
158
|
+
Configuration that preserves node identities and stoichiometric edge
|
|
159
|
+
semantics.
|
|
160
|
+
:rtype: SymmetryConfig
|
|
161
|
+
"""
|
|
162
|
+
return cls(
|
|
163
|
+
species_identity_keys=("smiles", "label"),
|
|
164
|
+
rule_identity_keys=("rule_repr", "rxn_repr", "label"),
|
|
165
|
+
edge_attr_keys=("role", "stoich"),
|
|
166
|
+
require_kind=True,
|
|
167
|
+
use_edge_direction=True,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def topological(cls) -> "SymmetryConfig":
|
|
172
|
+
"""Build a topology-focused symmetry configuration.
|
|
173
|
+
|
|
174
|
+
This mode ignores species and rule identities while still respecting
|
|
175
|
+
node kinds and selected edge annotations.
|
|
176
|
+
|
|
177
|
+
:return:
|
|
178
|
+
Configuration emphasizing graph topology over node identity.
|
|
179
|
+
:rtype: SymmetryConfig
|
|
180
|
+
"""
|
|
181
|
+
return cls(
|
|
182
|
+
species_identity_keys=(),
|
|
183
|
+
rule_identity_keys=(),
|
|
184
|
+
species_extra_attr_keys=(),
|
|
185
|
+
rule_extra_attr_keys=(),
|
|
186
|
+
other_node_attr_keys=(),
|
|
187
|
+
generic_node_attr_keys=(),
|
|
188
|
+
edge_attr_keys=("role", "stoich"),
|
|
189
|
+
require_kind=True,
|
|
190
|
+
use_edge_direction=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass(frozen=True)
|
|
195
|
+
class WLResult:
|
|
196
|
+
"""
|
|
197
|
+
Result of Weisfeiler-Lehman refinement.
|
|
198
|
+
|
|
199
|
+
:param colors:
|
|
200
|
+
Final color assigned to each node.
|
|
201
|
+
:type colors: Dict[Any, str]
|
|
202
|
+
|
|
203
|
+
:param orbits:
|
|
204
|
+
Approximate orbit partition induced by final color classes.
|
|
205
|
+
:type orbits: List[Set[Any]]
|
|
206
|
+
|
|
207
|
+
:param color_hist:
|
|
208
|
+
Histogram of final colors.
|
|
209
|
+
:type color_hist: Dict[str, int]
|
|
210
|
+
|
|
211
|
+
:param iters_run:
|
|
212
|
+
Number of WL iterations actually executed.
|
|
213
|
+
:type iters_run: int
|
|
214
|
+
|
|
215
|
+
:param stabilized:
|
|
216
|
+
Whether refinement stabilized before the iteration limit.
|
|
217
|
+
:type stabilized: bool
|
|
218
|
+
|
|
219
|
+
:param approx_automorphism_count:
|
|
220
|
+
Approximate automorphism count inferred from unresolved cells, if
|
|
221
|
+
available.
|
|
222
|
+
:type approx_automorphism_count: Optional[int]
|
|
223
|
+
|
|
224
|
+
:param canonical_order:
|
|
225
|
+
Canonical node ordering induced by WL colors and tie-breaking rules.
|
|
226
|
+
:type canonical_order: List[Any]
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
colors: Dict[Any, str]
|
|
230
|
+
orbits: List[Set[Any]]
|
|
231
|
+
color_hist: Dict[str, int]
|
|
232
|
+
iters_run: int
|
|
233
|
+
stabilized: bool
|
|
234
|
+
approx_automorphism_count: Optional[int]
|
|
235
|
+
canonical_order: List[Any]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass(frozen=True)
|
|
239
|
+
class AutomorphismResult:
|
|
240
|
+
"""
|
|
241
|
+
Result of an automorphism computation.
|
|
242
|
+
|
|
243
|
+
:param graph_type:
|
|
244
|
+
Graph representation type used in the computation.
|
|
245
|
+
:type graph_type: str
|
|
246
|
+
|
|
247
|
+
:param automorphism_count:
|
|
248
|
+
Number of discovered automorphisms.
|
|
249
|
+
:type automorphism_count: int
|
|
250
|
+
|
|
251
|
+
:param sample_mappings:
|
|
252
|
+
Sample node mappings realizing automorphisms.
|
|
253
|
+
:type sample_mappings: List[Dict[Any, Any]]
|
|
254
|
+
|
|
255
|
+
:param orbits:
|
|
256
|
+
Orbit partition induced by the sampled mappings.
|
|
257
|
+
:type orbits: List[Set[Any]]
|
|
258
|
+
|
|
259
|
+
:param elapsed_seconds:
|
|
260
|
+
Runtime in seconds.
|
|
261
|
+
:type elapsed_seconds: float
|
|
262
|
+
|
|
263
|
+
:param stopped_early:
|
|
264
|
+
Whether the computation stopped early due to timeout or count limit.
|
|
265
|
+
:type stopped_early: bool
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
graph_type: str
|
|
269
|
+
automorphism_count: int
|
|
270
|
+
sample_mappings: List[Dict[Any, Any]]
|
|
271
|
+
orbits: List[Set[Any]]
|
|
272
|
+
elapsed_seconds: float
|
|
273
|
+
stopped_early: bool
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@dataclass(frozen=True)
|
|
277
|
+
class IsomorphismResult:
|
|
278
|
+
"""
|
|
279
|
+
Result of an isomorphism test.
|
|
280
|
+
|
|
281
|
+
:param isomorphic:
|
|
282
|
+
Whether the compared graphs are isomorphic.
|
|
283
|
+
:type isomorphic: bool
|
|
284
|
+
|
|
285
|
+
:param mapping:
|
|
286
|
+
Witness mapping from the first graph to the second graph, if found.
|
|
287
|
+
:type mapping: Optional[Dict[Any, Any]]
|
|
288
|
+
|
|
289
|
+
:param elapsed_seconds:
|
|
290
|
+
Runtime in seconds.
|
|
291
|
+
:type elapsed_seconds: float
|
|
292
|
+
|
|
293
|
+
:param rejected_by_invariants:
|
|
294
|
+
Whether the comparison was rejected using fast invariants before an exact
|
|
295
|
+
search.
|
|
296
|
+
:type rejected_by_invariants: bool
|
|
297
|
+
|
|
298
|
+
:param mode:
|
|
299
|
+
Matching mode identifier, for example semantic or topological.
|
|
300
|
+
:type mode: str
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
isomorphic: bool
|
|
304
|
+
mapping: Optional[Dict[Any, Any]]
|
|
305
|
+
elapsed_seconds: float
|
|
306
|
+
rejected_by_invariants: bool
|
|
307
|
+
mode: str
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@dataclass(frozen=True)
|
|
311
|
+
class CanonicalResult:
|
|
312
|
+
"""
|
|
313
|
+
Result of graph canonicalization.
|
|
314
|
+
|
|
315
|
+
:param canonical_order:
|
|
316
|
+
Canonical node ordering.
|
|
317
|
+
:type canonical_order: List[Any]
|
|
318
|
+
|
|
319
|
+
:param canonical_key:
|
|
320
|
+
Canonical key derived from the canonical order.
|
|
321
|
+
:type canonical_key: Tuple[Any, ...]
|
|
322
|
+
|
|
323
|
+
:param exact:
|
|
324
|
+
Whether the result is exact rather than approximate.
|
|
325
|
+
:type exact: bool
|
|
326
|
+
|
|
327
|
+
:param elapsed_seconds:
|
|
328
|
+
Runtime in seconds.
|
|
329
|
+
:type elapsed_seconds: float
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
canonical_order: List[Any]
|
|
333
|
+
canonical_key: Tuple[Any, ...]
|
|
334
|
+
exact: bool
|
|
335
|
+
elapsed_seconds: float
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def freeze(x: Any) -> Hashable:
|
|
339
|
+
"""Convert common container types into a stable hashable representation.
|
|
340
|
+
|
|
341
|
+
:param x:
|
|
342
|
+
Object to freeze.
|
|
343
|
+
:type x: Any
|
|
344
|
+
|
|
345
|
+
:return:
|
|
346
|
+
Hashable recursively frozen representation.
|
|
347
|
+
:rtype: Hashable
|
|
348
|
+
|
|
349
|
+
.. rubric:: Examples
|
|
350
|
+
|
|
351
|
+
.. code-block:: python
|
|
352
|
+
|
|
353
|
+
freeze({"b": [2, 1], "a": {"x", "y"}})
|
|
354
|
+
"""
|
|
355
|
+
if isinstance(x, dict):
|
|
356
|
+
return tuple(
|
|
357
|
+
(k, freeze(v)) for k, v in sorted(x.items(), key=lambda kv: str(kv[0]))
|
|
358
|
+
)
|
|
359
|
+
if isinstance(x, (list, tuple)):
|
|
360
|
+
return tuple(freeze(v) for v in x)
|
|
361
|
+
if isinstance(x, set):
|
|
362
|
+
return tuple(sorted((freeze(v) for v in x), key=str))
|
|
363
|
+
return x
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def hash_text(text: str, *, digest_size: int = 16) -> str:
|
|
367
|
+
"""Hash text with BLAKE2b.
|
|
368
|
+
|
|
369
|
+
:param text:
|
|
370
|
+
Input text.
|
|
371
|
+
:type text: str
|
|
372
|
+
|
|
373
|
+
:param digest_size:
|
|
374
|
+
Output digest size in bytes.
|
|
375
|
+
:type digest_size: int
|
|
376
|
+
|
|
377
|
+
:return:
|
|
378
|
+
Hex digest string.
|
|
379
|
+
:rtype: str
|
|
380
|
+
"""
|
|
381
|
+
h = hashlib.blake2b(digest_size=digest_size)
|
|
382
|
+
h.update(text.encode("utf-8", errors="replace"))
|
|
383
|
+
return h.hexdigest()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def should_stop(
|
|
387
|
+
start: float,
|
|
388
|
+
timeout_sec: Optional[float],
|
|
389
|
+
*,
|
|
390
|
+
count: Optional[int] = None,
|
|
391
|
+
max_count: Optional[int] = None,
|
|
392
|
+
) -> bool:
|
|
393
|
+
"""Decide whether a search should stop.
|
|
394
|
+
|
|
395
|
+
:param start:
|
|
396
|
+
Start time from :func:`perf_counter`.
|
|
397
|
+
:type start: float
|
|
398
|
+
|
|
399
|
+
:param timeout_sec:
|
|
400
|
+
Optional timeout in seconds.
|
|
401
|
+
:type timeout_sec: Optional[float]
|
|
402
|
+
|
|
403
|
+
:param count:
|
|
404
|
+
Current number of discovered solutions.
|
|
405
|
+
:type count: Optional[int]
|
|
406
|
+
|
|
407
|
+
:param max_count:
|
|
408
|
+
Optional maximum number of solutions to collect.
|
|
409
|
+
:type max_count: Optional[int]
|
|
410
|
+
|
|
411
|
+
:return:
|
|
412
|
+
``True`` if the search should stop.
|
|
413
|
+
:rtype: bool
|
|
414
|
+
"""
|
|
415
|
+
if timeout_sec is not None and perf_counter() - start > timeout_sec:
|
|
416
|
+
return True
|
|
417
|
+
if max_count is not None and count is not None and count >= max_count:
|
|
418
|
+
return True
|
|
419
|
+
return False
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _factorial_capped(n: int, cap: int) -> int:
|
|
423
|
+
"""Compute ``n!`` with an upper cap.
|
|
424
|
+
|
|
425
|
+
:param n:
|
|
426
|
+
Nonnegative integer.
|
|
427
|
+
:type n: int
|
|
428
|
+
|
|
429
|
+
:param cap:
|
|
430
|
+
Upper cap for early stopping.
|
|
431
|
+
:type cap: int
|
|
432
|
+
|
|
433
|
+
:return:
|
|
434
|
+
``min(n!, cap)``.
|
|
435
|
+
:rtype: int
|
|
436
|
+
"""
|
|
437
|
+
out = 1
|
|
438
|
+
for i in range(2, n + 1):
|
|
439
|
+
out *= i
|
|
440
|
+
if out >= cap:
|
|
441
|
+
return cap
|
|
442
|
+
return out
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _product_factorials_capped(values: Iterable[int], cap: int) -> int:
|
|
446
|
+
"""Compute a capped product of factorials.
|
|
447
|
+
|
|
448
|
+
:param values:
|
|
449
|
+
Iterable of cell sizes.
|
|
450
|
+
:type values: Iterable[int]
|
|
451
|
+
|
|
452
|
+
:param cap:
|
|
453
|
+
Upper cap for early stopping.
|
|
454
|
+
:type cap: int
|
|
455
|
+
|
|
456
|
+
:return:
|
|
457
|
+
Product of factorials, capped at ``cap``.
|
|
458
|
+
:rtype: int
|
|
459
|
+
"""
|
|
460
|
+
out = 1
|
|
461
|
+
for v in values:
|
|
462
|
+
out *= _factorial_capped(v, cap)
|
|
463
|
+
if out >= cap:
|
|
464
|
+
return cap
|
|
465
|
+
return out
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def approx_automorphism_count_from_cells(
|
|
469
|
+
cells: Iterable[Sequence[Any]], *, cap: int = 10**18
|
|
470
|
+
) -> int:
|
|
471
|
+
"""Approximate automorphism count from unresolved cell sizes.
|
|
472
|
+
|
|
473
|
+
This estimate assumes each cell can be permuted independently.
|
|
474
|
+
|
|
475
|
+
:param cells:
|
|
476
|
+
Iterable of partition cells.
|
|
477
|
+
:type cells: Iterable[Sequence[Any]]
|
|
478
|
+
|
|
479
|
+
:param cap:
|
|
480
|
+
Upper cap on the returned count.
|
|
481
|
+
:type cap: int
|
|
482
|
+
|
|
483
|
+
:return:
|
|
484
|
+
Approximate capped automorphism count.
|
|
485
|
+
:rtype: int
|
|
486
|
+
"""
|
|
487
|
+
return _product_factorials_capped((len(c) for c in cells), cap)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _copy_as_digraph(G: nx.Graph) -> nx.DiGraph:
|
|
491
|
+
"""Copy a NetworkX graph into a simple directed graph.
|
|
492
|
+
|
|
493
|
+
Undirected edges are duplicated in both directions. Multi-edges are merged
|
|
494
|
+
into a simple :class:`nx.DiGraph`.
|
|
495
|
+
|
|
496
|
+
:param G:
|
|
497
|
+
Input graph.
|
|
498
|
+
:type G: nx.Graph
|
|
499
|
+
|
|
500
|
+
:return:
|
|
501
|
+
Directed graph copy.
|
|
502
|
+
:rtype: nx.DiGraph
|
|
503
|
+
"""
|
|
504
|
+
if isinstance(G, nx.DiGraph):
|
|
505
|
+
return G.copy()
|
|
506
|
+
if isinstance(G, nx.MultiDiGraph):
|
|
507
|
+
H = nx.DiGraph()
|
|
508
|
+
H.add_nodes_from((n, dict(d)) for n, d in G.nodes(data=True))
|
|
509
|
+
for u, v, _, d in G.edges(keys=True, data=True):
|
|
510
|
+
if H.has_edge(u, v):
|
|
511
|
+
H[u][v].update(d)
|
|
512
|
+
else:
|
|
513
|
+
H.add_edge(u, v, **dict(d))
|
|
514
|
+
return H
|
|
515
|
+
if isinstance(G, nx.MultiGraph):
|
|
516
|
+
H = nx.DiGraph()
|
|
517
|
+
H.add_nodes_from((n, dict(d)) for n, d in G.nodes(data=True))
|
|
518
|
+
for u, v, _, d in G.edges(keys=True, data=True):
|
|
519
|
+
if not H.has_edge(u, v):
|
|
520
|
+
H.add_edge(u, v, **dict(d))
|
|
521
|
+
if not H.has_edge(v, u):
|
|
522
|
+
H.add_edge(v, u, **dict(d))
|
|
523
|
+
return H
|
|
524
|
+
H = nx.DiGraph()
|
|
525
|
+
H.add_nodes_from((n, dict(d)) for n, d in G.nodes(data=True))
|
|
526
|
+
for u, v, d in G.edges(data=True):
|
|
527
|
+
H.add_edge(u, v, **dict(d))
|
|
528
|
+
if not G.is_directed() and not H.has_edge(v, u):
|
|
529
|
+
H.add_edge(v, u, **dict(d))
|
|
530
|
+
return H
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def prepare_graph(
|
|
534
|
+
source: Any,
|
|
535
|
+
*,
|
|
536
|
+
include_rule: bool = True,
|
|
537
|
+
include_stoich: bool = True,
|
|
538
|
+
) -> Tuple[nx.DiGraph, str]:
|
|
539
|
+
"""Convert a supported source object into a directed graph.
|
|
540
|
+
|
|
541
|
+
Supported inputs are:
|
|
542
|
+
- a NetworkX graph
|
|
543
|
+
- an object exposing ``to_digraph()``
|
|
544
|
+
|
|
545
|
+
:param source:
|
|
546
|
+
Input graph-like object.
|
|
547
|
+
:type source: Any
|
|
548
|
+
|
|
549
|
+
:param include_rule:
|
|
550
|
+
Whether the resulting graph should be treated as bipartite
|
|
551
|
+
species-rule graph rather than a species-only graph.
|
|
552
|
+
:type include_rule: bool
|
|
553
|
+
|
|
554
|
+
:param include_stoich:
|
|
555
|
+
Reserved compatibility flag.
|
|
556
|
+
:type include_stoich: bool
|
|
557
|
+
|
|
558
|
+
:return:
|
|
559
|
+
Pair ``(graph, graph_type)``.
|
|
560
|
+
:rtype: Tuple[nx.DiGraph, str]
|
|
561
|
+
|
|
562
|
+
:raises TypeError:
|
|
563
|
+
If the input is unsupported.
|
|
564
|
+
"""
|
|
565
|
+
_ = include_stoich
|
|
566
|
+
|
|
567
|
+
if isinstance(source, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)):
|
|
568
|
+
return _copy_as_digraph(source), "bipartite" if include_rule else "species"
|
|
569
|
+
if hasattr(source, "to_digraph") and callable(source.to_digraph):
|
|
570
|
+
return _copy_as_digraph(source.to_digraph()), (
|
|
571
|
+
"bipartite" if include_rule else "species"
|
|
572
|
+
)
|
|
573
|
+
raise TypeError("Expected a NetworkX graph or an object with to_digraph().")
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _first_present(attrs: Mapping[str, Any], keys: Sequence[str]) -> Any:
|
|
577
|
+
"""Return the first non-``None`` attribute value among preferred keys.
|
|
578
|
+
|
|
579
|
+
:param attrs:
|
|
580
|
+
Attribute mapping.
|
|
581
|
+
:type attrs: Mapping[str, Any]
|
|
582
|
+
|
|
583
|
+
:param keys:
|
|
584
|
+
Candidate attribute keys in priority order.
|
|
585
|
+
:type keys: Sequence[str]
|
|
586
|
+
|
|
587
|
+
:return:
|
|
588
|
+
First present non-``None`` value, or ``None``.
|
|
589
|
+
:rtype: Any
|
|
590
|
+
"""
|
|
591
|
+
for k in keys:
|
|
592
|
+
v = attrs.get(k, None)
|
|
593
|
+
if v is not None:
|
|
594
|
+
return v
|
|
595
|
+
return None
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def node_kind(attrs: Mapping[str, Any], config: SymmetryConfig) -> Any:
|
|
599
|
+
"""Extract node kind from an attribute mapping.
|
|
600
|
+
|
|
601
|
+
:param attrs:
|
|
602
|
+
Node attribute mapping.
|
|
603
|
+
:type attrs: Mapping[str, Any]
|
|
604
|
+
|
|
605
|
+
:param config:
|
|
606
|
+
Symmetry configuration.
|
|
607
|
+
:type config: SymmetryConfig
|
|
608
|
+
|
|
609
|
+
:return:
|
|
610
|
+
Node kind value or ``None``.
|
|
611
|
+
:rtype: Any
|
|
612
|
+
|
|
613
|
+
.. note::
|
|
614
|
+
The legacy reaction spelling ``"rule"`` is normalized to
|
|
615
|
+
``config.rule_kind_value`` so that two otherwise identical networks do
|
|
616
|
+
not canonicalize differently merely because one spells reaction nodes
|
|
617
|
+
``kind="reaction"`` and the other ``kind="rule"``. See
|
|
618
|
+
:mod:`synkit.CRN.kinds`.
|
|
619
|
+
"""
|
|
620
|
+
kind = attrs.get(config.kind_attr_key, None)
|
|
621
|
+
if isinstance(kind, str) and kind.strip().lower() in REACTION_KINDS:
|
|
622
|
+
return config.rule_kind_value
|
|
623
|
+
return kind
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def _append_identity_token(
|
|
627
|
+
out: List[Any],
|
|
628
|
+
attrs: Mapping[str, Any],
|
|
629
|
+
identity_keys: Sequence[str],
|
|
630
|
+
config: SymmetryConfig,
|
|
631
|
+
) -> None:
|
|
632
|
+
"""Append an identity token to a node token list when available.
|
|
633
|
+
|
|
634
|
+
:param out:
|
|
635
|
+
Output token list to mutate.
|
|
636
|
+
:type out: List[Any]
|
|
637
|
+
|
|
638
|
+
:param attrs:
|
|
639
|
+
Node attributes.
|
|
640
|
+
:type attrs: Mapping[str, Any]
|
|
641
|
+
|
|
642
|
+
:param identity_keys:
|
|
643
|
+
Preferred identity keys.
|
|
644
|
+
:type identity_keys: Sequence[str]
|
|
645
|
+
|
|
646
|
+
:param config:
|
|
647
|
+
Symmetry configuration.
|
|
648
|
+
:type config: SymmetryConfig
|
|
649
|
+
|
|
650
|
+
:return:
|
|
651
|
+
``None``.
|
|
652
|
+
:rtype: None
|
|
653
|
+
"""
|
|
654
|
+
ident = _first_present(attrs, identity_keys)
|
|
655
|
+
if ident is not None:
|
|
656
|
+
out.append(("identity", freeze(ident)))
|
|
657
|
+
elif config.include_missing_keys and identity_keys:
|
|
658
|
+
out.append(("identity", None))
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def _append_selected_attrs(
|
|
662
|
+
out: List[Any],
|
|
663
|
+
attrs: Mapping[str, Any],
|
|
664
|
+
keys: Sequence[str],
|
|
665
|
+
ignored_keys: Sequence[str],
|
|
666
|
+
) -> None:
|
|
667
|
+
"""Append selected attributes to a token list.
|
|
668
|
+
|
|
669
|
+
:param out:
|
|
670
|
+
Output token list to mutate.
|
|
671
|
+
:type out: List[Any]
|
|
672
|
+
|
|
673
|
+
:param attrs:
|
|
674
|
+
Attribute mapping.
|
|
675
|
+
:type attrs: Mapping[str, Any]
|
|
676
|
+
|
|
677
|
+
:param keys:
|
|
678
|
+
Attribute keys to include.
|
|
679
|
+
:type keys: Sequence[str]
|
|
680
|
+
|
|
681
|
+
:param ignored_keys:
|
|
682
|
+
Attribute keys to skip.
|
|
683
|
+
:type ignored_keys: Sequence[str]
|
|
684
|
+
|
|
685
|
+
:return:
|
|
686
|
+
``None``.
|
|
687
|
+
:rtype: None
|
|
688
|
+
"""
|
|
689
|
+
for k in keys:
|
|
690
|
+
if k not in ignored_keys:
|
|
691
|
+
out.append((k, freeze(attrs.get(k, None))))
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def _species_node_token(
|
|
695
|
+
attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
|
|
696
|
+
) -> None:
|
|
697
|
+
"""Append species-specific token components.
|
|
698
|
+
|
|
699
|
+
:param attrs:
|
|
700
|
+
Node attributes.
|
|
701
|
+
:type attrs: Mapping[str, Any]
|
|
702
|
+
|
|
703
|
+
:param config:
|
|
704
|
+
Symmetry configuration.
|
|
705
|
+
:type config: SymmetryConfig
|
|
706
|
+
|
|
707
|
+
:param out:
|
|
708
|
+
Output token list to mutate.
|
|
709
|
+
:type out: List[Any]
|
|
710
|
+
|
|
711
|
+
:return:
|
|
712
|
+
``None``.
|
|
713
|
+
:rtype: None
|
|
714
|
+
"""
|
|
715
|
+
_append_identity_token(out, attrs, config.species_identity_keys, config)
|
|
716
|
+
_append_selected_attrs(
|
|
717
|
+
out,
|
|
718
|
+
attrs,
|
|
719
|
+
config.species_extra_attr_keys,
|
|
720
|
+
config.ignored_node_attr_keys,
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def _rule_node_token(
|
|
725
|
+
attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
|
|
726
|
+
) -> None:
|
|
727
|
+
"""Append rule-specific token components.
|
|
728
|
+
|
|
729
|
+
:param attrs:
|
|
730
|
+
Node attributes.
|
|
731
|
+
:type attrs: Mapping[str, Any]
|
|
732
|
+
|
|
733
|
+
:param config:
|
|
734
|
+
Symmetry configuration.
|
|
735
|
+
:type config: SymmetryConfig
|
|
736
|
+
|
|
737
|
+
:param out:
|
|
738
|
+
Output token list to mutate.
|
|
739
|
+
:type out: List[Any]
|
|
740
|
+
|
|
741
|
+
:return:
|
|
742
|
+
``None``.
|
|
743
|
+
:rtype: None
|
|
744
|
+
"""
|
|
745
|
+
_append_identity_token(out, attrs, config.rule_identity_keys, config)
|
|
746
|
+
_append_selected_attrs(
|
|
747
|
+
out,
|
|
748
|
+
attrs,
|
|
749
|
+
config.rule_extra_attr_keys,
|
|
750
|
+
config.ignored_node_attr_keys,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def _other_node_token(
|
|
755
|
+
attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
|
|
756
|
+
) -> None:
|
|
757
|
+
"""Append generic token components for non-species, non-rule nodes.
|
|
758
|
+
|
|
759
|
+
:param attrs:
|
|
760
|
+
Node attributes.
|
|
761
|
+
:type attrs: Mapping[str, Any]
|
|
762
|
+
|
|
763
|
+
:param config:
|
|
764
|
+
Symmetry configuration.
|
|
765
|
+
:type config: SymmetryConfig
|
|
766
|
+
|
|
767
|
+
:param out:
|
|
768
|
+
Output token list to mutate.
|
|
769
|
+
:type out: List[Any]
|
|
770
|
+
|
|
771
|
+
:return:
|
|
772
|
+
``None``.
|
|
773
|
+
:rtype: None
|
|
774
|
+
"""
|
|
775
|
+
_append_selected_attrs(
|
|
776
|
+
out,
|
|
777
|
+
attrs,
|
|
778
|
+
config.other_node_attr_keys,
|
|
779
|
+
config.ignored_node_attr_keys,
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def _append_generic_node_attrs(
|
|
784
|
+
attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
|
|
785
|
+
) -> None:
|
|
786
|
+
"""Append generic node attributes shared across all node kinds.
|
|
787
|
+
|
|
788
|
+
:param attrs:
|
|
789
|
+
Node attributes.
|
|
790
|
+
:type attrs: Mapping[str, Any]
|
|
791
|
+
|
|
792
|
+
:param config:
|
|
793
|
+
Symmetry configuration.
|
|
794
|
+
:type config: SymmetryConfig
|
|
795
|
+
|
|
796
|
+
:param out:
|
|
797
|
+
Output token list to mutate.
|
|
798
|
+
:type out: List[Any]
|
|
799
|
+
|
|
800
|
+
:return:
|
|
801
|
+
``None``.
|
|
802
|
+
:rtype: None
|
|
803
|
+
"""
|
|
804
|
+
for k in config.generic_node_attr_keys:
|
|
805
|
+
if k == config.kind_attr_key or k in config.ignored_node_attr_keys:
|
|
806
|
+
continue
|
|
807
|
+
out.append((k, freeze(attrs.get(k, None))))
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
def node_token(attrs: Mapping[str, Any], config: SymmetryConfig) -> Tuple[Any, ...]:
|
|
811
|
+
"""Build a canonical token for a node.
|
|
812
|
+
|
|
813
|
+
The token depends on node kind, configured identity keys, optional extra
|
|
814
|
+
attributes, and generic attributes shared by all node kinds.
|
|
815
|
+
|
|
816
|
+
:param attrs:
|
|
817
|
+
Node attribute mapping.
|
|
818
|
+
:type attrs: Mapping[str, Any]
|
|
819
|
+
|
|
820
|
+
:param config:
|
|
821
|
+
Symmetry configuration controlling token content.
|
|
822
|
+
:type config: SymmetryConfig
|
|
823
|
+
|
|
824
|
+
:return:
|
|
825
|
+
Immutable node token.
|
|
826
|
+
:rtype: Tuple[Any, ...]
|
|
827
|
+
|
|
828
|
+
.. rubric:: Examples
|
|
829
|
+
|
|
830
|
+
.. code-block:: python
|
|
831
|
+
|
|
832
|
+
cfg = SymmetryConfig.semantic()
|
|
833
|
+
tok = node_token({"kind": "species", "smiles": "CCO"}, cfg)
|
|
834
|
+
print(tok)
|
|
835
|
+
"""
|
|
836
|
+
kind = node_kind(attrs, config)
|
|
837
|
+
out: List[Any] = []
|
|
838
|
+
|
|
839
|
+
if config.require_kind:
|
|
840
|
+
out.append((config.kind_attr_key, freeze(kind)))
|
|
841
|
+
|
|
842
|
+
if kind == config.species_kind_value:
|
|
843
|
+
_species_node_token(attrs, config, out)
|
|
844
|
+
elif kind == config.rule_kind_value:
|
|
845
|
+
_rule_node_token(attrs, config, out)
|
|
846
|
+
else:
|
|
847
|
+
_other_node_token(attrs, config, out)
|
|
848
|
+
|
|
849
|
+
_append_generic_node_attrs(attrs, config, out)
|
|
850
|
+
return tuple(out)
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def edge_token(attrs: Mapping[str, Any], config: SymmetryConfig) -> Tuple[Any, ...]:
|
|
854
|
+
"""Build a canonical token for an edge.
|
|
855
|
+
|
|
856
|
+
:param attrs:
|
|
857
|
+
Edge attribute mapping.
|
|
858
|
+
:type attrs: Mapping[str, Any]
|
|
859
|
+
|
|
860
|
+
:param config:
|
|
861
|
+
Symmetry configuration.
|
|
862
|
+
:type config: SymmetryConfig
|
|
863
|
+
|
|
864
|
+
:return:
|
|
865
|
+
Immutable edge token.
|
|
866
|
+
:rtype: Tuple[Any, ...]
|
|
867
|
+
|
|
868
|
+
.. rubric:: Examples
|
|
869
|
+
|
|
870
|
+
.. code-block:: python
|
|
871
|
+
|
|
872
|
+
cfg = SymmetryConfig.semantic()
|
|
873
|
+
tok = edge_token({"role": "reactant", "stoich": 2}, cfg)
|
|
874
|
+
"""
|
|
875
|
+
out: List[Any] = []
|
|
876
|
+
for k in config.edge_attr_keys:
|
|
877
|
+
if k in config.ignored_edge_attr_keys:
|
|
878
|
+
continue
|
|
879
|
+
out.append((k, freeze(attrs.get(k, None))))
|
|
880
|
+
return tuple(out)
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
def node_matcher(
|
|
884
|
+
config: SymmetryConfig,
|
|
885
|
+
) -> Callable[[Dict[str, Any], Dict[str, Any]], bool]:
|
|
886
|
+
"""Build a node matcher function based on :func:`node_token`.
|
|
887
|
+
|
|
888
|
+
:param config:
|
|
889
|
+
Symmetry configuration.
|
|
890
|
+
:type config: SymmetryConfig
|
|
891
|
+
|
|
892
|
+
:return:
|
|
893
|
+
Predicate comparing two node attribute dictionaries.
|
|
894
|
+
:rtype: Callable[[Dict[str, Any], Dict[str, Any]], bool]
|
|
895
|
+
"""
|
|
896
|
+
|
|
897
|
+
def match(a1: Dict[str, Any], a2: Dict[str, Any]) -> bool:
|
|
898
|
+
return node_token(a1, config) == node_token(a2, config)
|
|
899
|
+
|
|
900
|
+
return match
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
def edge_matcher(
|
|
904
|
+
config: SymmetryConfig,
|
|
905
|
+
) -> Callable[[Dict[str, Any], Dict[str, Any]], bool]:
|
|
906
|
+
"""Build an edge matcher function based on :func:`edge_token`.
|
|
907
|
+
|
|
908
|
+
:param config:
|
|
909
|
+
Symmetry configuration.
|
|
910
|
+
:type config: SymmetryConfig
|
|
911
|
+
|
|
912
|
+
:return:
|
|
913
|
+
Predicate comparing two edge attribute dictionaries.
|
|
914
|
+
:rtype: Callable[[Dict[str, Any], Dict[str, Any]], bool]
|
|
915
|
+
"""
|
|
916
|
+
|
|
917
|
+
def match(a1: Dict[str, Any], a2: Dict[str, Any]) -> bool:
|
|
918
|
+
return edge_token(a1, config) == edge_token(a2, config)
|
|
919
|
+
|
|
920
|
+
return match
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def build_fast_signature(
|
|
924
|
+
G: nx.DiGraph,
|
|
925
|
+
graph_type: str,
|
|
926
|
+
config: SymmetryConfig,
|
|
927
|
+
*,
|
|
928
|
+
wl_color_hist: Optional[Mapping[str, int]] = None,
|
|
929
|
+
) -> Tuple[Any, ...]:
|
|
930
|
+
"""Build a fast graph signature for cheap rejection tests.
|
|
931
|
+
|
|
932
|
+
The signature includes graph size, node-token histogram, edge-token
|
|
933
|
+
histogram, in/out-degree histogram, and optional WL color histogram.
|
|
934
|
+
|
|
935
|
+
:param G:
|
|
936
|
+
Directed graph.
|
|
937
|
+
:type G: nx.DiGraph
|
|
938
|
+
|
|
939
|
+
:param graph_type:
|
|
940
|
+
Graph type label.
|
|
941
|
+
:type graph_type: str
|
|
942
|
+
|
|
943
|
+
:param config:
|
|
944
|
+
Symmetry configuration.
|
|
945
|
+
:type config: SymmetryConfig
|
|
946
|
+
|
|
947
|
+
:param wl_color_hist:
|
|
948
|
+
Optional WL color histogram.
|
|
949
|
+
:type wl_color_hist: Optional[Mapping[str, int]]
|
|
950
|
+
|
|
951
|
+
:return:
|
|
952
|
+
Fast signature tuple.
|
|
953
|
+
:rtype: Tuple[Any, ...]
|
|
954
|
+
"""
|
|
955
|
+
node_hist = Counter(node_token(G.nodes[v], config) for v in G.nodes())
|
|
956
|
+
edge_hist = Counter(edge_token(attrs, config) for _, _, attrs in G.edges(data=True))
|
|
957
|
+
deg_hist = Counter((G.in_degree(v), G.out_degree(v)) for v in G.nodes())
|
|
958
|
+
return (
|
|
959
|
+
graph_type,
|
|
960
|
+
G.number_of_nodes(),
|
|
961
|
+
G.number_of_edges(),
|
|
962
|
+
tuple(sorted(node_hist.items(), key=str)),
|
|
963
|
+
tuple(sorted(edge_hist.items(), key=str)),
|
|
964
|
+
tuple(sorted(deg_hist.items(), key=str)),
|
|
965
|
+
tuple(sorted((wl_color_hist or {}).items(), key=str)),
|
|
966
|
+
)
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
def graph_key_from_order(
|
|
970
|
+
G: nx.DiGraph, order: Sequence[Any], config: SymmetryConfig
|
|
971
|
+
) -> Tuple[Any, ...]:
|
|
972
|
+
"""Build a canonical graph key from a node order.
|
|
973
|
+
|
|
974
|
+
:param G:
|
|
975
|
+
Directed graph.
|
|
976
|
+
:type G: nx.DiGraph
|
|
977
|
+
|
|
978
|
+
:param order:
|
|
979
|
+
Node order.
|
|
980
|
+
:type order: Sequence[Any]
|
|
981
|
+
|
|
982
|
+
:param config:
|
|
983
|
+
Symmetry configuration.
|
|
984
|
+
:type config: SymmetryConfig
|
|
985
|
+
|
|
986
|
+
:return:
|
|
987
|
+
Canonical graph key.
|
|
988
|
+
:rtype: Tuple[Any, ...]
|
|
989
|
+
"""
|
|
990
|
+
pos = {v: i for i, v in enumerate(order)}
|
|
991
|
+
node_part = tuple((i, node_token(G.nodes[v], config)) for i, v in enumerate(order))
|
|
992
|
+
edge_rows: List[Tuple[int, int, Tuple[Any, ...]]] = []
|
|
993
|
+
for u, v, attrs in G.edges(data=True):
|
|
994
|
+
edge_rows.append((pos[u], pos[v], edge_token(attrs, config)))
|
|
995
|
+
edge_rows.sort(key=lambda x: (x[0], x[1], str(x[2])))
|
|
996
|
+
return (node_part, tuple(edge_rows))
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
def invert_mapping(mapping: Dict[Any, Any]) -> Dict[Any, Any]:
|
|
1000
|
+
"""Invert a one-to-one mapping.
|
|
1001
|
+
|
|
1002
|
+
:param mapping:
|
|
1003
|
+
Mapping to invert.
|
|
1004
|
+
:type mapping: Dict[Any, Any]
|
|
1005
|
+
|
|
1006
|
+
:return:
|
|
1007
|
+
Inverted mapping.
|
|
1008
|
+
:rtype: Dict[Any, Any]
|
|
1009
|
+
"""
|
|
1010
|
+
return {v: k for k, v in mapping.items()}
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
def orbits_from_mappings(
|
|
1014
|
+
nodes: Sequence[Any], mappings: Iterable[Dict[Any, Any]]
|
|
1015
|
+
) -> List[Set[Any]]:
|
|
1016
|
+
"""Build orbit classes from a collection of automorphism mappings.
|
|
1017
|
+
|
|
1018
|
+
:param nodes:
|
|
1019
|
+
Nodes whose orbit partition should be computed.
|
|
1020
|
+
:type nodes: Sequence[Any]
|
|
1021
|
+
|
|
1022
|
+
:param mappings:
|
|
1023
|
+
Automorphism mappings.
|
|
1024
|
+
:type mappings: Iterable[Dict[Any, Any]]
|
|
1025
|
+
|
|
1026
|
+
:return:
|
|
1027
|
+
List of orbit sets.
|
|
1028
|
+
:rtype: List[Set[Any]]
|
|
1029
|
+
|
|
1030
|
+
.. rubric:: Examples
|
|
1031
|
+
|
|
1032
|
+
.. code-block:: python
|
|
1033
|
+
|
|
1034
|
+
nodes = ["A", "B", "C"]
|
|
1035
|
+
maps = [{"A": "B", "B": "A", "C": "C"}]
|
|
1036
|
+
print(orbits_from_mappings(nodes, maps))
|
|
1037
|
+
"""
|
|
1038
|
+
parent: Dict[Any, Any] = {n: n for n in nodes}
|
|
1039
|
+
|
|
1040
|
+
def find(x: Any) -> Any:
|
|
1041
|
+
while parent[x] != x:
|
|
1042
|
+
parent[x] = parent[parent[x]]
|
|
1043
|
+
x = parent[x]
|
|
1044
|
+
return x
|
|
1045
|
+
|
|
1046
|
+
def union(a: Any, b: Any) -> None:
|
|
1047
|
+
ra, rb = find(a), find(b)
|
|
1048
|
+
if ra != rb:
|
|
1049
|
+
parent[rb] = ra
|
|
1050
|
+
|
|
1051
|
+
for m in mappings:
|
|
1052
|
+
for a, b in m.items():
|
|
1053
|
+
if a in parent and b in parent:
|
|
1054
|
+
union(a, b)
|
|
1055
|
+
|
|
1056
|
+
buckets: Dict[Any, Set[Any]] = defaultdict(set)
|
|
1057
|
+
for n in nodes:
|
|
1058
|
+
buckets[find(n)].add(n)
|
|
1059
|
+
return list(buckets.values())
|