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,402 @@
|
|
|
1
|
+
"""Branch-and-bound enumeration of minimal siphons and traps.
|
|
2
|
+
|
|
3
|
+
Enumerating siphons by testing all :math:`2^n` place subsets is only viable for
|
|
4
|
+
toy networks: a 25-species network already takes about a minute, and the cost
|
|
5
|
+
multiplies by roughly eight for every three further species.
|
|
6
|
+
|
|
7
|
+
This module instead builds on the *closure* operator: for any set of places
|
|
8
|
+
``A`` there is a unique **maximal siphon contained in** ``A``, and it can be
|
|
9
|
+
computed in polynomial time by repeatedly discarding places that break the
|
|
10
|
+
siphon condition (:func:`max_siphon_within`). Minimal siphons are then reached
|
|
11
|
+
by starting from the maximal siphon of the whole net and repeatedly removing
|
|
12
|
+
one place and re-closing.
|
|
13
|
+
|
|
14
|
+
That descent is complete. If ``M`` is a minimal siphon and ``S`` is any siphon
|
|
15
|
+
with ``M`` a strict subset of ``S``, then for a place ``p`` in ``S`` but not in
|
|
16
|
+
``M`` the set ``S \\ {p}`` still contains ``M``; since ``M`` is itself a siphon,
|
|
17
|
+
it survives the closure, so ``max_siphon_within(S \\ {p})`` still contains
|
|
18
|
+
``M``. Every minimal siphon is therefore reachable from the top by successive
|
|
19
|
+
delete-and-close steps.
|
|
20
|
+
|
|
21
|
+
Traps are the exact dual (every transition consuming from the set also produces
|
|
22
|
+
back into it) and are handled by the same machinery.
|
|
23
|
+
|
|
24
|
+
.. rubric:: Example
|
|
25
|
+
|
|
26
|
+
.. code-block:: python
|
|
27
|
+
|
|
28
|
+
from synkit.CRN.Structure import SynCRN
|
|
29
|
+
from synkit.CRN.Petrinet import PetriNet
|
|
30
|
+
from synkit.CRN.Petrinet.siphon_search import minimal_siphons
|
|
31
|
+
|
|
32
|
+
net = PetriNet.from_syncrn(SynCRN.from_reaction_strings(["A>>B", "B>>A"]))
|
|
33
|
+
print(minimal_siphons(net))
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
from typing import Dict, FrozenSet, List, Optional, Sequence, Set, Tuple
|
|
39
|
+
|
|
40
|
+
from .net import PetriNet
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"max_siphon_within",
|
|
44
|
+
"max_trap_within",
|
|
45
|
+
"minimal_siphons",
|
|
46
|
+
"minimal_traps",
|
|
47
|
+
"SiphonSearchLimit",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
#: Default ceiling on explored search nodes, so a pathological network degrades
|
|
51
|
+
#: into a truncated result rather than an apparent hang.
|
|
52
|
+
DEFAULT_MAX_NODES = 200_000
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SiphonSearchLimit(RuntimeError):
|
|
56
|
+
"""Raised when a siphon or trap search exceeds its exploration budget."""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _incidence(
|
|
60
|
+
net: PetriNet,
|
|
61
|
+
) -> Tuple[Dict[str, Set[str]], Dict[str, Set[str]]]:
|
|
62
|
+
"""Index a Petri net as ``transition -> consumed`` / ``transition -> produced``.
|
|
63
|
+
|
|
64
|
+
:param net:
|
|
65
|
+
Petri net to index.
|
|
66
|
+
:type net: PetriNet
|
|
67
|
+
|
|
68
|
+
:return:
|
|
69
|
+
Pair ``(pre, post)`` mapping each transition id to the set of places it
|
|
70
|
+
consumes from and produces into, with zero-weight arcs dropped.
|
|
71
|
+
:rtype: Tuple[Dict[str, Set[str]], Dict[str, Set[str]]]
|
|
72
|
+
"""
|
|
73
|
+
pre: Dict[str, Set[str]] = {}
|
|
74
|
+
post: Dict[str, Set[str]] = {}
|
|
75
|
+
|
|
76
|
+
for tid in net.transition_order:
|
|
77
|
+
t = net.transitions[tid]
|
|
78
|
+
pre[tid] = {p for p, w in t.pre.items() if w > 0}
|
|
79
|
+
post[tid] = {p for p, w in t.post.items() if w > 0}
|
|
80
|
+
|
|
81
|
+
return pre, post
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _max_closed_within(
|
|
85
|
+
allowed: Set[str],
|
|
86
|
+
*,
|
|
87
|
+
trigger: Dict[str, Set[str]],
|
|
88
|
+
required: Dict[str, Set[str]],
|
|
89
|
+
transition_order: Sequence[str],
|
|
90
|
+
) -> Set[str]:
|
|
91
|
+
"""Compute the maximal subset of ``allowed`` closed under a incidence rule.
|
|
92
|
+
|
|
93
|
+
A set ``S`` is closed when, for every transition ``t``, ``trigger[t]``
|
|
94
|
+
meeting ``S`` implies ``required[t]`` also meets ``S``. Choosing
|
|
95
|
+
``trigger=post`` and ``required=pre`` yields siphons; swapping them yields
|
|
96
|
+
traps.
|
|
97
|
+
|
|
98
|
+
Places are removed until a fixed point is reached, which is the unique
|
|
99
|
+
maximal closed subset.
|
|
100
|
+
|
|
101
|
+
:param allowed:
|
|
102
|
+
Candidate place set. Not mutated.
|
|
103
|
+
:type allowed: Set[str]
|
|
104
|
+
|
|
105
|
+
:param trigger:
|
|
106
|
+
Transition-to-places map that activates the condition.
|
|
107
|
+
:type trigger: Dict[str, Set[str]]
|
|
108
|
+
|
|
109
|
+
:param required:
|
|
110
|
+
Transition-to-places map that must then also be met.
|
|
111
|
+
:type required: Dict[str, Set[str]]
|
|
112
|
+
|
|
113
|
+
:param transition_order:
|
|
114
|
+
Deterministic transition iteration order.
|
|
115
|
+
:type transition_order: Sequence[str]
|
|
116
|
+
|
|
117
|
+
:return:
|
|
118
|
+
Maximal closed subset of ``allowed``, possibly empty.
|
|
119
|
+
:rtype: Set[str]
|
|
120
|
+
"""
|
|
121
|
+
current = set(allowed)
|
|
122
|
+
|
|
123
|
+
changed = True
|
|
124
|
+
while changed and current:
|
|
125
|
+
changed = False
|
|
126
|
+
for tid in transition_order:
|
|
127
|
+
hit = trigger[tid] & current
|
|
128
|
+
if not hit:
|
|
129
|
+
continue
|
|
130
|
+
if required[tid] & current:
|
|
131
|
+
continue
|
|
132
|
+
# This transition breaks the condition: every place it triggers on
|
|
133
|
+
# must leave the set.
|
|
134
|
+
current -= hit
|
|
135
|
+
changed = True
|
|
136
|
+
if not current:
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
return current
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def max_siphon_within(net: PetriNet, allowed: Set[str]) -> Set[str]:
|
|
143
|
+
"""Return the unique maximal siphon contained in ``allowed``.
|
|
144
|
+
|
|
145
|
+
:param net:
|
|
146
|
+
Petri net to analyse.
|
|
147
|
+
:type net: PetriNet
|
|
148
|
+
|
|
149
|
+
:param allowed:
|
|
150
|
+
Places the siphon may draw from.
|
|
151
|
+
:type allowed: Set[str]
|
|
152
|
+
|
|
153
|
+
:return:
|
|
154
|
+
Maximal siphon inside ``allowed``, empty if there is none.
|
|
155
|
+
:rtype: Set[str]
|
|
156
|
+
|
|
157
|
+
.. rubric:: Example
|
|
158
|
+
|
|
159
|
+
.. code-block:: python
|
|
160
|
+
|
|
161
|
+
max_siphon_within(net, set(net.place_order))
|
|
162
|
+
"""
|
|
163
|
+
pre, post = _incidence(net)
|
|
164
|
+
return _max_closed_within(
|
|
165
|
+
allowed,
|
|
166
|
+
trigger=post,
|
|
167
|
+
required=pre,
|
|
168
|
+
transition_order=net.transition_order,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def max_trap_within(net: PetriNet, allowed: Set[str]) -> Set[str]:
|
|
173
|
+
"""Return the unique maximal trap contained in ``allowed``.
|
|
174
|
+
|
|
175
|
+
:param net:
|
|
176
|
+
Petri net to analyse.
|
|
177
|
+
:type net: PetriNet
|
|
178
|
+
|
|
179
|
+
:param allowed:
|
|
180
|
+
Places the trap may draw from.
|
|
181
|
+
:type allowed: Set[str]
|
|
182
|
+
|
|
183
|
+
:return:
|
|
184
|
+
Maximal trap inside ``allowed``, empty if there is none.
|
|
185
|
+
:rtype: Set[str]
|
|
186
|
+
|
|
187
|
+
.. rubric:: Example
|
|
188
|
+
|
|
189
|
+
.. code-block:: python
|
|
190
|
+
|
|
191
|
+
max_trap_within(net, set(net.place_order))
|
|
192
|
+
"""
|
|
193
|
+
pre, post = _incidence(net)
|
|
194
|
+
return _max_closed_within(
|
|
195
|
+
allowed,
|
|
196
|
+
trigger=pre,
|
|
197
|
+
required=post,
|
|
198
|
+
transition_order=net.transition_order,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _enumerate_minimal(
|
|
203
|
+
net: PetriNet,
|
|
204
|
+
*,
|
|
205
|
+
trigger: Dict[str, Set[str]],
|
|
206
|
+
required: Dict[str, Set[str]],
|
|
207
|
+
max_size: Optional[int],
|
|
208
|
+
max_nodes: int,
|
|
209
|
+
strict_limit: bool,
|
|
210
|
+
) -> List[Set[str]]:
|
|
211
|
+
"""Enumerate inclusion-minimal closed sets by delete-and-close descent.
|
|
212
|
+
|
|
213
|
+
:param net:
|
|
214
|
+
Petri net to analyse.
|
|
215
|
+
:type net: PetriNet
|
|
216
|
+
|
|
217
|
+
:param trigger:
|
|
218
|
+
Transition-to-places map activating the closure condition.
|
|
219
|
+
:type trigger: Dict[str, Set[str]]
|
|
220
|
+
|
|
221
|
+
:param required:
|
|
222
|
+
Transition-to-places map that must also be met.
|
|
223
|
+
:type required: Dict[str, Set[str]]
|
|
224
|
+
|
|
225
|
+
:param max_size:
|
|
226
|
+
Optional ceiling on the size of returned sets.
|
|
227
|
+
:type max_size: Optional[int]
|
|
228
|
+
|
|
229
|
+
:param max_nodes:
|
|
230
|
+
Maximum number of closure evaluations before giving up.
|
|
231
|
+
:type max_nodes: int
|
|
232
|
+
|
|
233
|
+
:param strict_limit:
|
|
234
|
+
Whether exceeding ``max_nodes`` raises instead of truncating.
|
|
235
|
+
:type strict_limit: bool
|
|
236
|
+
|
|
237
|
+
:return:
|
|
238
|
+
Inclusion-minimal closed sets.
|
|
239
|
+
:rtype: List[Set[str]]
|
|
240
|
+
|
|
241
|
+
:raises SiphonSearchLimit:
|
|
242
|
+
If the budget is exhausted and ``strict_limit`` is set.
|
|
243
|
+
"""
|
|
244
|
+
places = list(net.place_order)
|
|
245
|
+
order = net.transition_order
|
|
246
|
+
|
|
247
|
+
root = _max_closed_within(
|
|
248
|
+
set(places), trigger=trigger, required=required, transition_order=order
|
|
249
|
+
)
|
|
250
|
+
if not root:
|
|
251
|
+
return []
|
|
252
|
+
|
|
253
|
+
seen: Set[FrozenSet[str]] = set()
|
|
254
|
+
minimal: List[Set[str]] = []
|
|
255
|
+
stack: List[FrozenSet[str]] = [frozenset(root)]
|
|
256
|
+
seen.add(frozenset(root))
|
|
257
|
+
nodes = 0
|
|
258
|
+
truncated = False
|
|
259
|
+
|
|
260
|
+
while stack:
|
|
261
|
+
current = stack.pop()
|
|
262
|
+
|
|
263
|
+
is_minimal = True
|
|
264
|
+
for p in sorted(current):
|
|
265
|
+
if nodes >= max_nodes:
|
|
266
|
+
truncated = True
|
|
267
|
+
break
|
|
268
|
+
nodes += 1
|
|
269
|
+
|
|
270
|
+
child = _max_closed_within(
|
|
271
|
+
set(current) - {p},
|
|
272
|
+
trigger=trigger,
|
|
273
|
+
required=required,
|
|
274
|
+
transition_order=order,
|
|
275
|
+
)
|
|
276
|
+
if not child:
|
|
277
|
+
continue
|
|
278
|
+
|
|
279
|
+
# A non-empty proper closed subset exists, so `current` is not
|
|
280
|
+
# inclusion-minimal.
|
|
281
|
+
is_minimal = False
|
|
282
|
+
key = frozenset(child)
|
|
283
|
+
if key not in seen:
|
|
284
|
+
seen.add(key)
|
|
285
|
+
stack.append(key)
|
|
286
|
+
|
|
287
|
+
if truncated:
|
|
288
|
+
break
|
|
289
|
+
if is_minimal:
|
|
290
|
+
minimal.append(set(current))
|
|
291
|
+
|
|
292
|
+
if truncated and strict_limit:
|
|
293
|
+
raise SiphonSearchLimit(
|
|
294
|
+
f"Search exceeded {max_nodes} closure evaluations. Raise max_nodes, "
|
|
295
|
+
f"set max_size, or pass strict_limit=False to accept a partial result."
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# The descent can surface a set that strictly contains another minimal one
|
|
299
|
+
# when the budget truncates a branch; filter defensively.
|
|
300
|
+
result = [
|
|
301
|
+
s
|
|
302
|
+
for s in minimal
|
|
303
|
+
if not any(t < s for t in minimal)
|
|
304
|
+
and (max_size is None or len(s) <= max_size)
|
|
305
|
+
]
|
|
306
|
+
result.sort(key=lambda s: (len(s), sorted(s)))
|
|
307
|
+
return result
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def minimal_siphons(
|
|
311
|
+
net: PetriNet,
|
|
312
|
+
*,
|
|
313
|
+
max_size: Optional[int] = None,
|
|
314
|
+
max_nodes: int = DEFAULT_MAX_NODES,
|
|
315
|
+
strict_limit: bool = False,
|
|
316
|
+
) -> List[Set[str]]:
|
|
317
|
+
"""Enumerate the inclusion-minimal siphons of a Petri net.
|
|
318
|
+
|
|
319
|
+
:param net:
|
|
320
|
+
Petri net to analyse.
|
|
321
|
+
:type net: PetriNet
|
|
322
|
+
|
|
323
|
+
:param max_size:
|
|
324
|
+
Optional ceiling on siphon size. Filtering happens after enumeration,
|
|
325
|
+
so the result is exactly the minimal siphons no larger than the bound.
|
|
326
|
+
:type max_size: Optional[int]
|
|
327
|
+
|
|
328
|
+
:param max_nodes:
|
|
329
|
+
Maximum number of closure evaluations before the search stops.
|
|
330
|
+
:type max_nodes: int
|
|
331
|
+
|
|
332
|
+
:param strict_limit:
|
|
333
|
+
Raise :class:`SiphonSearchLimit` instead of returning a partial result
|
|
334
|
+
when the budget is exhausted.
|
|
335
|
+
:type strict_limit: bool
|
|
336
|
+
|
|
337
|
+
:return:
|
|
338
|
+
Minimal siphons, ordered by size then lexicographically.
|
|
339
|
+
:rtype: List[Set[str]]
|
|
340
|
+
|
|
341
|
+
.. rubric:: Example
|
|
342
|
+
|
|
343
|
+
.. code-block:: python
|
|
344
|
+
|
|
345
|
+
minimal_siphons(net, max_size=4)
|
|
346
|
+
"""
|
|
347
|
+
pre, post = _incidence(net)
|
|
348
|
+
return _enumerate_minimal(
|
|
349
|
+
net,
|
|
350
|
+
trigger=post,
|
|
351
|
+
required=pre,
|
|
352
|
+
max_size=max_size,
|
|
353
|
+
max_nodes=max_nodes,
|
|
354
|
+
strict_limit=strict_limit,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def minimal_traps(
|
|
359
|
+
net: PetriNet,
|
|
360
|
+
*,
|
|
361
|
+
max_size: Optional[int] = None,
|
|
362
|
+
max_nodes: int = DEFAULT_MAX_NODES,
|
|
363
|
+
strict_limit: bool = False,
|
|
364
|
+
) -> List[Set[str]]:
|
|
365
|
+
"""Enumerate the inclusion-minimal traps of a Petri net.
|
|
366
|
+
|
|
367
|
+
:param net:
|
|
368
|
+
Petri net to analyse.
|
|
369
|
+
:type net: PetriNet
|
|
370
|
+
|
|
371
|
+
:param max_size:
|
|
372
|
+
Optional ceiling on trap size.
|
|
373
|
+
:type max_size: Optional[int]
|
|
374
|
+
|
|
375
|
+
:param max_nodes:
|
|
376
|
+
Maximum number of closure evaluations before the search stops.
|
|
377
|
+
:type max_nodes: int
|
|
378
|
+
|
|
379
|
+
:param strict_limit:
|
|
380
|
+
Raise :class:`SiphonSearchLimit` instead of returning a partial result
|
|
381
|
+
when the budget is exhausted.
|
|
382
|
+
:type strict_limit: bool
|
|
383
|
+
|
|
384
|
+
:return:
|
|
385
|
+
Minimal traps, ordered by size then lexicographically.
|
|
386
|
+
:rtype: List[Set[str]]
|
|
387
|
+
|
|
388
|
+
.. rubric:: Example
|
|
389
|
+
|
|
390
|
+
.. code-block:: python
|
|
391
|
+
|
|
392
|
+
minimal_traps(net)
|
|
393
|
+
"""
|
|
394
|
+
pre, post = _incidence(net)
|
|
395
|
+
return _enumerate_minimal(
|
|
396
|
+
net,
|
|
397
|
+
trigger=pre,
|
|
398
|
+
required=post,
|
|
399
|
+
max_size=max_size,
|
|
400
|
+
max_nodes=max_nodes,
|
|
401
|
+
strict_limit=strict_limit,
|
|
402
|
+
)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional, Set
|
|
4
|
+
|
|
5
|
+
from .net import PetriNet
|
|
6
|
+
from .siphon_search import DEFAULT_MAX_NODES, minimal_siphons, minimal_traps
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _as_petri(crn: Any) -> PetriNet:
|
|
10
|
+
"""Convert a SynCRN-like object into a :class:`PetriNet`.
|
|
11
|
+
|
|
12
|
+
If ``crn`` is already a :class:`PetriNet`, it is returned unchanged.
|
|
13
|
+
Otherwise, the object is converted using :meth:`PetriNet.from_syncrn`.
|
|
14
|
+
|
|
15
|
+
:param crn: SynCRN-like object or an existing :class:`PetriNet`.
|
|
16
|
+
:type crn: Any
|
|
17
|
+
:return: Petri-net representation of the input object.
|
|
18
|
+
:rtype: PetriNet
|
|
19
|
+
"""
|
|
20
|
+
return crn if isinstance(crn, PetriNet) else PetriNet.from_syncrn(crn)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _is_siphon(net: PetriNet, places: Set[str]) -> bool:
|
|
24
|
+
"""Test whether a set of places is a siphon.
|
|
25
|
+
|
|
26
|
+
A place set ``S`` is a siphon if every transition that produces tokens into
|
|
27
|
+
``S`` also consumes at least one token from ``S``.
|
|
28
|
+
|
|
29
|
+
:param net: Petri net to inspect.
|
|
30
|
+
:type net: PetriNet
|
|
31
|
+
:param places: Candidate place set.
|
|
32
|
+
:type places: Set[str]
|
|
33
|
+
:return:
|
|
34
|
+
``True`` if ``places`` is a non-empty siphon, otherwise ``False``.
|
|
35
|
+
:rtype: bool
|
|
36
|
+
"""
|
|
37
|
+
if not places:
|
|
38
|
+
return False
|
|
39
|
+
for tid in net.transition_order:
|
|
40
|
+
t = net.transitions[tid]
|
|
41
|
+
produces_into_s = any(p in places and w > 0 for p, w in t.post.items())
|
|
42
|
+
if not produces_into_s:
|
|
43
|
+
continue
|
|
44
|
+
consumes_from_s = any(p in places and w > 0 for p, w in t.pre.items())
|
|
45
|
+
if not consumes_from_s:
|
|
46
|
+
return False
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _is_trap(net: PetriNet, places: Set[str]) -> bool:
|
|
51
|
+
"""Test whether a set of places is a trap.
|
|
52
|
+
|
|
53
|
+
A place set ``S`` is a trap if every transition that consumes tokens from
|
|
54
|
+
``S`` also produces at least one token back into ``S``.
|
|
55
|
+
|
|
56
|
+
:param net: Petri net to inspect.
|
|
57
|
+
:type net: PetriNet
|
|
58
|
+
:param places: Candidate place set.
|
|
59
|
+
:type places: Set[str]
|
|
60
|
+
:return:
|
|
61
|
+
``True`` if ``places`` is a non-empty trap, otherwise ``False``.
|
|
62
|
+
:rtype: bool
|
|
63
|
+
"""
|
|
64
|
+
if not places:
|
|
65
|
+
return False
|
|
66
|
+
for tid in net.transition_order:
|
|
67
|
+
t = net.transitions[tid]
|
|
68
|
+
consumes_from_s = any(p in places and w > 0 for p, w in t.pre.items())
|
|
69
|
+
if not consumes_from_s:
|
|
70
|
+
continue
|
|
71
|
+
produces_into_s = any(p in places and w > 0 for p, w in t.post.items())
|
|
72
|
+
if not produces_into_s:
|
|
73
|
+
return False
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _minimal_sets(candidates: List[Set[str]]) -> List[Set[str]]:
|
|
78
|
+
"""Reduce a collection of candidate sets to inclusion-minimal sets.
|
|
79
|
+
|
|
80
|
+
The result contains only those sets for which no strict subset is also
|
|
81
|
+
present among the candidates.
|
|
82
|
+
|
|
83
|
+
:param candidates: Candidate place sets.
|
|
84
|
+
:type candidates: List[Set[str]]
|
|
85
|
+
:return: Inclusion-minimal subset of the input candidates.
|
|
86
|
+
:rtype: List[Set[str]]
|
|
87
|
+
"""
|
|
88
|
+
candidates = sorted(candidates, key=lambda s: (len(s), sorted(s)))
|
|
89
|
+
out: List[Set[str]] = []
|
|
90
|
+
for s in candidates:
|
|
91
|
+
if any(t.issubset(s) for t in out):
|
|
92
|
+
continue
|
|
93
|
+
out = [t for t in out if not s.issubset(t)]
|
|
94
|
+
out.append(s)
|
|
95
|
+
return out
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _render_place_sets(
|
|
99
|
+
net: PetriNet,
|
|
100
|
+
sets_: List[Set[str]],
|
|
101
|
+
*,
|
|
102
|
+
names: str,
|
|
103
|
+
) -> List[Set[str]]:
|
|
104
|
+
"""Render place sets either by internal ids or by place labels.
|
|
105
|
+
|
|
106
|
+
:param net: Petri net providing place-name lookup.
|
|
107
|
+
:type net: PetriNet
|
|
108
|
+
:param sets_: Place sets expressed in internal place ids.
|
|
109
|
+
:type sets_: List[Set[str]]
|
|
110
|
+
:param names:
|
|
111
|
+
Output naming mode. Supported values are ``"id"`` and ``"label"``.
|
|
112
|
+
:type names: str
|
|
113
|
+
:return:
|
|
114
|
+
Place sets rendered according to the requested naming convention.
|
|
115
|
+
:rtype: List[Set[str]]
|
|
116
|
+
:raises ValueError: If ``names`` is not ``"id"`` or ``"label"``.
|
|
117
|
+
"""
|
|
118
|
+
if names == "id":
|
|
119
|
+
return [set(s) for s in sets_]
|
|
120
|
+
if names == "label":
|
|
121
|
+
return [{net.place_name(p) for p in s} for s in sets_]
|
|
122
|
+
raise ValueError("names must be 'id' or 'label'")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def find_siphons(
|
|
126
|
+
crn: Any,
|
|
127
|
+
*,
|
|
128
|
+
max_size: Optional[int] = None,
|
|
129
|
+
names: str = "label",
|
|
130
|
+
max_nodes: int = DEFAULT_MAX_NODES,
|
|
131
|
+
strict_limit: bool = False,
|
|
132
|
+
) -> List[Set[str]]:
|
|
133
|
+
"""Enumerate inclusion-minimal siphons of a SynCRN Petri-net view.
|
|
134
|
+
|
|
135
|
+
The search uses the maximal-siphon closure operator with branch-and-bound
|
|
136
|
+
descent rather than brute-force subset enumeration; see
|
|
137
|
+
:mod:`synkit.CRN.Petrinet.siphon_search`.
|
|
138
|
+
|
|
139
|
+
:param crn: SynCRN-like object or :class:`PetriNet`.
|
|
140
|
+
:type crn: Any
|
|
141
|
+
:param max_size:
|
|
142
|
+
Maximum siphon size to consider. ``None`` means all sizes.
|
|
143
|
+
:type max_size: int | None
|
|
144
|
+
:param names:
|
|
145
|
+
Whether to return internal place ids or species labels.
|
|
146
|
+
Supported values are ``"id"`` and ``"label"``.
|
|
147
|
+
:type names: str
|
|
148
|
+
:param max_nodes:
|
|
149
|
+
Exploration budget for the branch-and-bound search.
|
|
150
|
+
:type max_nodes: int
|
|
151
|
+
:param strict_limit:
|
|
152
|
+
Raise :class:`~synkit.CRN.Petrinet.siphon_search.SiphonSearchLimit`
|
|
153
|
+
instead of returning a partial result when the budget is exhausted.
|
|
154
|
+
:type strict_limit: bool
|
|
155
|
+
:return: Inclusion-minimal siphons.
|
|
156
|
+
:rtype: List[Set[str]]
|
|
157
|
+
"""
|
|
158
|
+
from ..backend import resolve_backend
|
|
159
|
+
|
|
160
|
+
backend, library = resolve_backend()
|
|
161
|
+
if backend == "cpp":
|
|
162
|
+
from ..native import enumerate_structural_sets
|
|
163
|
+
from .siphon_search import SiphonSearchLimit
|
|
164
|
+
|
|
165
|
+
net = _as_petri(crn)
|
|
166
|
+
result = enumerate_structural_sets(
|
|
167
|
+
net, kind="siphon", backend="cpp", library_path=library, max_nodes=max_nodes
|
|
168
|
+
)
|
|
169
|
+
if not result.complete:
|
|
170
|
+
error = SiphonSearchLimit(result.termination_reason)
|
|
171
|
+
error.result = result
|
|
172
|
+
raise error
|
|
173
|
+
sets_ = [
|
|
174
|
+
set(group)
|
|
175
|
+
for group in result.value["sets"]
|
|
176
|
+
if max_size is None or len(group) <= max_size
|
|
177
|
+
]
|
|
178
|
+
return _render_place_sets(net, sets_, names=names)
|
|
179
|
+
net = _as_petri(crn)
|
|
180
|
+
sets_ = minimal_siphons(
|
|
181
|
+
net,
|
|
182
|
+
max_size=max_size,
|
|
183
|
+
max_nodes=max_nodes,
|
|
184
|
+
strict_limit=strict_limit,
|
|
185
|
+
)
|
|
186
|
+
return _render_place_sets(net, sets_, names=names)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def find_traps(
|
|
190
|
+
crn: Any,
|
|
191
|
+
*,
|
|
192
|
+
max_size: Optional[int] = None,
|
|
193
|
+
names: str = "label",
|
|
194
|
+
max_nodes: int = DEFAULT_MAX_NODES,
|
|
195
|
+
strict_limit: bool = False,
|
|
196
|
+
) -> List[Set[str]]:
|
|
197
|
+
"""Enumerate inclusion-minimal traps of a SynCRN Petri-net view.
|
|
198
|
+
|
|
199
|
+
The search uses the maximal-siphon closure operator with branch-and-bound
|
|
200
|
+
descent rather than brute-force subset enumeration; see
|
|
201
|
+
:mod:`synkit.CRN.Petrinet.siphon_search`.
|
|
202
|
+
|
|
203
|
+
:param crn: SynCRN-like object or :class:`PetriNet`.
|
|
204
|
+
:type crn: Any
|
|
205
|
+
:param max_size:
|
|
206
|
+
Maximum trap size to consider. ``None`` means all sizes.
|
|
207
|
+
:type max_size: int | None
|
|
208
|
+
:param names:
|
|
209
|
+
Whether to return internal place ids or species labels.
|
|
210
|
+
Supported values are ``"id"`` and ``"label"``.
|
|
211
|
+
:type names: str
|
|
212
|
+
:param max_nodes:
|
|
213
|
+
Exploration budget for the branch-and-bound search.
|
|
214
|
+
:type max_nodes: int
|
|
215
|
+
:param strict_limit:
|
|
216
|
+
Raise :class:`~synkit.CRN.Petrinet.siphon_search.SiphonSearchLimit`
|
|
217
|
+
instead of returning a partial result when the budget is exhausted.
|
|
218
|
+
:type strict_limit: bool
|
|
219
|
+
:return: Inclusion-minimal traps.
|
|
220
|
+
:rtype: List[Set[str]]
|
|
221
|
+
"""
|
|
222
|
+
from ..backend import resolve_backend
|
|
223
|
+
|
|
224
|
+
backend, library = resolve_backend()
|
|
225
|
+
if backend == "cpp":
|
|
226
|
+
from ..native import enumerate_structural_sets
|
|
227
|
+
from .siphon_search import SiphonSearchLimit
|
|
228
|
+
|
|
229
|
+
net = _as_petri(crn)
|
|
230
|
+
result = enumerate_structural_sets(
|
|
231
|
+
net, kind="trap", backend="cpp", library_path=library, max_nodes=max_nodes
|
|
232
|
+
)
|
|
233
|
+
if not result.complete:
|
|
234
|
+
error = SiphonSearchLimit(result.termination_reason)
|
|
235
|
+
error.result = result
|
|
236
|
+
raise error
|
|
237
|
+
sets_ = [
|
|
238
|
+
set(group)
|
|
239
|
+
for group in result.value["sets"]
|
|
240
|
+
if max_size is None or len(group) <= max_size
|
|
241
|
+
]
|
|
242
|
+
return _render_place_sets(net, sets_, names=names)
|
|
243
|
+
net = _as_petri(crn)
|
|
244
|
+
sets_ = minimal_traps(
|
|
245
|
+
net,
|
|
246
|
+
max_size=max_size,
|
|
247
|
+
max_nodes=max_nodes,
|
|
248
|
+
strict_limit=strict_limit,
|
|
249
|
+
)
|
|
250
|
+
return _render_place_sets(net, sets_, names=names)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def species_transition_neighborhoods(crn: Any) -> Dict[str, Dict[str, List[str]]]:
|
|
254
|
+
"""Return per-species producer and consumer transition neighborhoods.
|
|
255
|
+
|
|
256
|
+
This is a small structural helper that is often useful when debugging
|
|
257
|
+
siphons, traps, and reachability issues.
|
|
258
|
+
|
|
259
|
+
The returned dictionary is keyed by internal place id. For each place, the
|
|
260
|
+
value contains the display label, the transitions that produce tokens into
|
|
261
|
+
the place, and the transitions that consume tokens from it.
|
|
262
|
+
|
|
263
|
+
:param crn: SynCRN-like object or :class:`PetriNet`.
|
|
264
|
+
:type crn: Any
|
|
265
|
+
:return:
|
|
266
|
+
Mapping from place id to a dictionary with keys ``"label"``,
|
|
267
|
+
``"producer_transitions"``, and ``"consumer_transitions"``.
|
|
268
|
+
:rtype: Dict[str, Dict[str, List[str]]]
|
|
269
|
+
"""
|
|
270
|
+
net = _as_petri(crn)
|
|
271
|
+
out: Dict[str, Dict[str, List[str]]] = {}
|
|
272
|
+
for p in net.place_order:
|
|
273
|
+
producers: List[str] = []
|
|
274
|
+
consumers: List[str] = []
|
|
275
|
+
for tid in net.transition_order:
|
|
276
|
+
t = net.transitions[tid]
|
|
277
|
+
if p in t.post:
|
|
278
|
+
producers.append(tid)
|
|
279
|
+
if p in t.pre:
|
|
280
|
+
consumers.append(tid)
|
|
281
|
+
out[p] = {
|
|
282
|
+
"label": net.place_name(p),
|
|
283
|
+
"producer_transitions": producers,
|
|
284
|
+
"consumer_transitions": consumers,
|
|
285
|
+
}
|
|
286
|
+
return out
|