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,796 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Dict, Iterable, List, Mapping, Optional, Set, Tuple
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from ._adapter import tokenize_syncrn_incidence
|
|
9
|
+
|
|
10
|
+
Multiset = Dict[str, int]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ReachabilityConfig:
|
|
15
|
+
"""Configuration for forward reachability traversal.
|
|
16
|
+
|
|
17
|
+
This configuration controls how many propagation layers are explored and
|
|
18
|
+
whether the traversal should terminate as soon as no newly reachable
|
|
19
|
+
species are discovered.
|
|
20
|
+
|
|
21
|
+
:param max_layers:
|
|
22
|
+
Maximum number of forward layers to compute.
|
|
23
|
+
:type max_layers: int
|
|
24
|
+
:param stop_when_no_new_species:
|
|
25
|
+
If ``True``, stop once a layer produces no newly reachable species,
|
|
26
|
+
even if reactions are still enabled.
|
|
27
|
+
:type stop_when_no_new_species: bool
|
|
28
|
+
|
|
29
|
+
.. rubric:: Example
|
|
30
|
+
|
|
31
|
+
.. code-block:: python
|
|
32
|
+
|
|
33
|
+
cfg = ReachabilityConfig(
|
|
34
|
+
max_layers=100,
|
|
35
|
+
stop_when_no_new_species=True,
|
|
36
|
+
)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
max_layers: int = 10_000
|
|
40
|
+
stop_when_no_new_species: bool = True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class ReachabilityLayer:
|
|
45
|
+
"""One forward reachability layer.
|
|
46
|
+
|
|
47
|
+
A layer summarizes what became newly active at a given traversal depth.
|
|
48
|
+
In set semantics, a reaction is considered enabled when all of its
|
|
49
|
+
reactant species are already reachable. In multiset semantics, a reaction
|
|
50
|
+
is enabled when the current marking contains sufficient multiplicity for
|
|
51
|
+
each reactant.
|
|
52
|
+
|
|
53
|
+
:param depth:
|
|
54
|
+
Layer index, starting at ``1`` for the first propagation step.
|
|
55
|
+
:type depth: int
|
|
56
|
+
:param newly_enabled_reactions:
|
|
57
|
+
Reaction identifiers that became enabled at this layer.
|
|
58
|
+
:type newly_enabled_reactions: List[str]
|
|
59
|
+
:param newly_reachable_species:
|
|
60
|
+
Species that became reachable for the first time at this layer.
|
|
61
|
+
:type newly_reachable_species: List[str]
|
|
62
|
+
:param all_reachable_species:
|
|
63
|
+
Complete set of reachable species after this layer is applied.
|
|
64
|
+
:type all_reachable_species: List[str]
|
|
65
|
+
|
|
66
|
+
.. rubric:: Example
|
|
67
|
+
|
|
68
|
+
.. code-block:: python
|
|
69
|
+
|
|
70
|
+
layer = ReachabilityLayer(
|
|
71
|
+
depth=2,
|
|
72
|
+
newly_enabled_reactions=["r3", "r4"],
|
|
73
|
+
newly_reachable_species=["E", "F"],
|
|
74
|
+
all_reachable_species=["A", "B", "C", "D", "E", "F"],
|
|
75
|
+
)
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
depth: int
|
|
79
|
+
newly_enabled_reactions: List[str]
|
|
80
|
+
newly_reachable_species: List[str]
|
|
81
|
+
all_reachable_species: List[str]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class ReachabilityResult:
|
|
86
|
+
"""Container for forward reachability results.
|
|
87
|
+
|
|
88
|
+
This object stores the initial support, the layered traversal trace, and
|
|
89
|
+
the first depth at which each species or reaction became reachable or
|
|
90
|
+
enabled.
|
|
91
|
+
|
|
92
|
+
:param initial_species:
|
|
93
|
+
Initial reachable species support used to seed the traversal.
|
|
94
|
+
:type initial_species: List[str]
|
|
95
|
+
:param layers:
|
|
96
|
+
Ordered list of reachability layers.
|
|
97
|
+
:type layers: List[ReachabilityLayer]
|
|
98
|
+
:param species_first_depth:
|
|
99
|
+
Mapping from species identifier to the first layer depth at which that
|
|
100
|
+
species became reachable. Initial species are assigned depth ``0``.
|
|
101
|
+
:type species_first_depth: Dict[str, int]
|
|
102
|
+
:param reaction_first_depth:
|
|
103
|
+
Mapping from reaction identifier to the first layer depth at which that
|
|
104
|
+
reaction became enabled.
|
|
105
|
+
:type reaction_first_depth: Dict[str, int]
|
|
106
|
+
|
|
107
|
+
.. rubric:: Example
|
|
108
|
+
|
|
109
|
+
.. code-block:: python
|
|
110
|
+
|
|
111
|
+
result = ReachabilityResult(
|
|
112
|
+
initial_species=["A", "B"],
|
|
113
|
+
layers=[],
|
|
114
|
+
species_first_depth={"A": 0, "B": 0},
|
|
115
|
+
reaction_first_depth={},
|
|
116
|
+
)
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
initial_species: List[str]
|
|
120
|
+
layers: List[ReachabilityLayer]
|
|
121
|
+
species_first_depth: Dict[str, int]
|
|
122
|
+
reaction_first_depth: Dict[str, int]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class PathwayReachability:
|
|
126
|
+
"""Forward reachability utilities for SynCRN pathway analysis.
|
|
127
|
+
|
|
128
|
+
This class provides layered forward propagation over a reaction hypergraph.
|
|
129
|
+
It supports two related semantics:
|
|
130
|
+
|
|
131
|
+
- **Set semantics**:
|
|
132
|
+
a reaction is enabled once all reactant species are present in the
|
|
133
|
+
reachable set; stoichiometric multiplicities are ignored for enabling.
|
|
134
|
+
- **Multiset semantics**:
|
|
135
|
+
a reaction is enabled only when the current marking contains enough
|
|
136
|
+
copies of each reactant species to satisfy stoichiometric coefficients.
|
|
137
|
+
|
|
138
|
+
The class can be loaded either from tokenized ``vertices`` / ``edges``
|
|
139
|
+
data or directly from a SynCRN-like object via :meth:`load_syncrn`.
|
|
140
|
+
|
|
141
|
+
Internally, reactions are stored as a mapping
|
|
142
|
+
|
|
143
|
+
``reaction_id -> (tail_multiset, head_multiset)``
|
|
144
|
+
|
|
145
|
+
where the tail is the reactant multiset and the head is the product
|
|
146
|
+
multiset.
|
|
147
|
+
|
|
148
|
+
:param config:
|
|
149
|
+
Optional reachability configuration. If omitted, a default
|
|
150
|
+
:class:`ReachabilityConfig` is used.
|
|
151
|
+
:type config: Optional[ReachabilityConfig]
|
|
152
|
+
|
|
153
|
+
.. rubric:: Example
|
|
154
|
+
|
|
155
|
+
.. code-block:: python
|
|
156
|
+
|
|
157
|
+
rr = PathwayReachability()
|
|
158
|
+
rr.load_hypergraph(
|
|
159
|
+
vertices=["A", "B", "C", "D"],
|
|
160
|
+
edges={
|
|
161
|
+
"r1": ({"A": 1, "B": 1}, {"C": 1}),
|
|
162
|
+
"r2": ({"C": 1}, {"D": 1}),
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
result = rr.compute_layers_set(initial_species=["A", "B"])
|
|
167
|
+
print(result.species_first_depth)
|
|
168
|
+
# {'A': 0, 'B': 0, 'C': 1, 'D': 2}
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(self, config: Optional[ReachabilityConfig] = None) -> None:
|
|
172
|
+
"""
|
|
173
|
+
Initialize an empty reachability engine.
|
|
174
|
+
|
|
175
|
+
:param config:
|
|
176
|
+
Optional reachability configuration.
|
|
177
|
+
:type config: Optional[ReachabilityConfig]
|
|
178
|
+
"""
|
|
179
|
+
self.vertices: Set[str] = set()
|
|
180
|
+
self.edges: Dict[str, Tuple[Multiset, Multiset]] = {}
|
|
181
|
+
self.species_token_to_id: Dict[str, str] = {}
|
|
182
|
+
self.reaction_token_to_id: Dict[str, str] = {}
|
|
183
|
+
self.species_id_to_token: Dict[str, str] = {}
|
|
184
|
+
self.reaction_id_to_token: Dict[str, str] = {}
|
|
185
|
+
self._config = config or ReachabilityConfig()
|
|
186
|
+
|
|
187
|
+
# ------------------------------------------------------------------
|
|
188
|
+
# data loading
|
|
189
|
+
# ------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
def load_hypergraph(
|
|
192
|
+
self,
|
|
193
|
+
vertices: Iterable[str],
|
|
194
|
+
edges: Mapping[str, Tuple[Mapping[str, int], Mapping[str, int]]],
|
|
195
|
+
) -> "PathwayReachability":
|
|
196
|
+
"""Load a tokenized reaction hypergraph directly.
|
|
197
|
+
|
|
198
|
+
Each edge must map a reaction identifier to a pair
|
|
199
|
+
``(tail_multiset, head_multiset)``, where both multisets map species
|
|
200
|
+
identifiers to strictly positive stoichiometric coefficients.
|
|
201
|
+
|
|
202
|
+
Non-positive coefficients are discarded during normalization.
|
|
203
|
+
|
|
204
|
+
:param vertices:
|
|
205
|
+
Iterable of species identifiers.
|
|
206
|
+
:type vertices: Iterable[str]
|
|
207
|
+
:param edges:
|
|
208
|
+
Mapping from reaction identifier to
|
|
209
|
+
``(reactant_multiset, product_multiset)``.
|
|
210
|
+
:type edges: Mapping[str, Tuple[Mapping[str, int], Mapping[str, int]]]
|
|
211
|
+
:return:
|
|
212
|
+
The current instance, to allow fluent chaining.
|
|
213
|
+
:rtype: PathwayReachability
|
|
214
|
+
|
|
215
|
+
.. rubric:: Example
|
|
216
|
+
|
|
217
|
+
.. code-block:: python
|
|
218
|
+
|
|
219
|
+
rr = PathwayReachability().load_hypergraph(
|
|
220
|
+
vertices=["A", "B", "C"],
|
|
221
|
+
edges={
|
|
222
|
+
"r1": ({"A": 1}, {"B": 1}),
|
|
223
|
+
"r2": ({"B": 1}, {"C": 1}),
|
|
224
|
+
},
|
|
225
|
+
)
|
|
226
|
+
"""
|
|
227
|
+
self.vertices = set(str(v) for v in vertices)
|
|
228
|
+
self.edges = {
|
|
229
|
+
str(eid): (
|
|
230
|
+
{str(s): int(v) for s, v in tail.items() if int(v) > 0},
|
|
231
|
+
{str(s): int(v) for s, v in head.items() if int(v) > 0},
|
|
232
|
+
)
|
|
233
|
+
for eid, (tail, head) in edges.items()
|
|
234
|
+
}
|
|
235
|
+
self.species_token_to_id = {v: v for v in self.vertices}
|
|
236
|
+
self.reaction_token_to_id = {eid: eid for eid in self.edges}
|
|
237
|
+
self.species_id_to_token = {v: v for v in self.vertices}
|
|
238
|
+
self.reaction_id_to_token = {eid: eid for eid in self.edges}
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
def load_syncrn(
|
|
242
|
+
self,
|
|
243
|
+
crn: object,
|
|
244
|
+
*,
|
|
245
|
+
species: str = "label",
|
|
246
|
+
reaction: str = "id",
|
|
247
|
+
) -> "PathwayReachability":
|
|
248
|
+
"""Load reachability data from a SynCRN-like object.
|
|
249
|
+
|
|
250
|
+
This method delegates tokenization to
|
|
251
|
+
:func:`tokenize_syncrn_incidence`, then stores both the tokenized
|
|
252
|
+
hypergraph and the forward/backward token-id lookup tables.
|
|
253
|
+
|
|
254
|
+
:param crn:
|
|
255
|
+
SynCRN-like object to tokenize.
|
|
256
|
+
:type crn: object
|
|
257
|
+
:param species:
|
|
258
|
+
Species attribute used during tokenization, such as ``"label"`` or
|
|
259
|
+
another species node attribute supported by the adapter.
|
|
260
|
+
:type species: str
|
|
261
|
+
:param reaction:
|
|
262
|
+
Reaction attribute used during tokenization, such as ``"id"``.
|
|
263
|
+
:type reaction: str
|
|
264
|
+
:return:
|
|
265
|
+
The current instance, to allow fluent chaining.
|
|
266
|
+
:rtype: PathwayReachability
|
|
267
|
+
|
|
268
|
+
.. rubric:: Example
|
|
269
|
+
|
|
270
|
+
.. code-block:: python
|
|
271
|
+
|
|
272
|
+
rr = PathwayReachability().load_syncrn(
|
|
273
|
+
syncrn_object,
|
|
274
|
+
species="label",
|
|
275
|
+
reaction="id",
|
|
276
|
+
)
|
|
277
|
+
"""
|
|
278
|
+
vertices, edges, species_token_to_id, reaction_token_to_id, _ = (
|
|
279
|
+
tokenize_syncrn_incidence(
|
|
280
|
+
crn,
|
|
281
|
+
species=species,
|
|
282
|
+
reaction=reaction,
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
self.load_hypergraph(vertices, edges)
|
|
286
|
+
self.species_token_to_id = species_token_to_id
|
|
287
|
+
self.reaction_token_to_id = reaction_token_to_id
|
|
288
|
+
self.species_id_to_token = {
|
|
289
|
+
sid: tok for tok, sid in species_token_to_id.items()
|
|
290
|
+
}
|
|
291
|
+
self.reaction_id_to_token = {
|
|
292
|
+
rid: tok for tok, rid in reaction_token_to_id.items()
|
|
293
|
+
}
|
|
294
|
+
return self
|
|
295
|
+
|
|
296
|
+
# ------------------------------------------------------------------
|
|
297
|
+
# small helpers
|
|
298
|
+
# ------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def _normalize_marking(marking: Mapping[str, int]) -> Dict[str, int]:
|
|
302
|
+
"""Normalize a marking into a clean positive multiset.
|
|
303
|
+
|
|
304
|
+
Species with non-positive counts are removed.
|
|
305
|
+
|
|
306
|
+
:param marking:
|
|
307
|
+
Input species-count mapping.
|
|
308
|
+
:type marking: Mapping[str, int]
|
|
309
|
+
:return:
|
|
310
|
+
Normalized marking containing only strictly positive counts.
|
|
311
|
+
:rtype: Dict[str, int]
|
|
312
|
+
"""
|
|
313
|
+
return {str(s): int(v) for s, v in marking.items() if int(v) > 0}
|
|
314
|
+
|
|
315
|
+
@staticmethod
|
|
316
|
+
def _support(marking: Mapping[str, int]) -> Set[str]:
|
|
317
|
+
"""Return the support of a marking.
|
|
318
|
+
|
|
319
|
+
The support is the set of species whose count is strictly positive.
|
|
320
|
+
|
|
321
|
+
:param marking:
|
|
322
|
+
Input species-count mapping.
|
|
323
|
+
:type marking: Mapping[str, int]
|
|
324
|
+
:return:
|
|
325
|
+
Set of species present with positive multiplicity.
|
|
326
|
+
:rtype: Set[str]
|
|
327
|
+
"""
|
|
328
|
+
return {s for s, v in marking.items() if int(v) > 0}
|
|
329
|
+
|
|
330
|
+
def _enabled_reactions_for_set(self, reachable: Set[str]) -> List[str]:
|
|
331
|
+
"""Return reactions enabled under set semantics.
|
|
332
|
+
|
|
333
|
+
A reaction is enabled if all reactant species are already members of
|
|
334
|
+
the reachable set. Stoichiometric coefficients are ignored for the
|
|
335
|
+
enabling condition.
|
|
336
|
+
|
|
337
|
+
:param reachable:
|
|
338
|
+
Current reachable species set.
|
|
339
|
+
:type reachable: Set[str]
|
|
340
|
+
:return:
|
|
341
|
+
List of enabled reaction identifiers.
|
|
342
|
+
:rtype: List[str]
|
|
343
|
+
"""
|
|
344
|
+
enabled: List[str] = []
|
|
345
|
+
for eid, (tail, _) in self.edges.items():
|
|
346
|
+
if set(tail.keys()).issubset(reachable):
|
|
347
|
+
enabled.append(eid)
|
|
348
|
+
return enabled
|
|
349
|
+
|
|
350
|
+
def _enabled_reactions_for_marking(self, marking: Mapping[str, int]) -> List[str]:
|
|
351
|
+
"""Return reactions enabled under multiset semantics.
|
|
352
|
+
|
|
353
|
+
A reaction is enabled if the marking contains at least the required
|
|
354
|
+
stoichiometric coefficient for every reactant species.
|
|
355
|
+
|
|
356
|
+
:param marking:
|
|
357
|
+
Current species marking.
|
|
358
|
+
:type marking: Mapping[str, int]
|
|
359
|
+
:return:
|
|
360
|
+
List of enabled reaction identifiers.
|
|
361
|
+
:rtype: List[str]
|
|
362
|
+
"""
|
|
363
|
+
enabled: List[str] = []
|
|
364
|
+
for eid, (tail, _) in self.edges.items():
|
|
365
|
+
if all(int(marking.get(s, 0)) >= int(coeff) for s, coeff in tail.items()):
|
|
366
|
+
enabled.append(eid)
|
|
367
|
+
return enabled
|
|
368
|
+
|
|
369
|
+
def _fire_reactions_once_in_batch(
|
|
370
|
+
self,
|
|
371
|
+
marking: Mapping[str, int],
|
|
372
|
+
reaction_ids: Iterable[str],
|
|
373
|
+
) -> Dict[str, int]:
|
|
374
|
+
"""Fire a batch of reactions once each from the given marking.
|
|
375
|
+
|
|
376
|
+
Reactants are consumed and products are produced exactly once for each
|
|
377
|
+
reaction in ``reaction_ids``. The resulting marking is normalized so
|
|
378
|
+
that non-positive counts are removed.
|
|
379
|
+
|
|
380
|
+
:param marking:
|
|
381
|
+
Input marking before firing.
|
|
382
|
+
:type marking: Mapping[str, int]
|
|
383
|
+
:param reaction_ids:
|
|
384
|
+
Iterable of reaction identifiers to fire once.
|
|
385
|
+
:type reaction_ids: Iterable[str]
|
|
386
|
+
:return:
|
|
387
|
+
Updated normalized marking after the batch firing.
|
|
388
|
+
:rtype: Dict[str, int]
|
|
389
|
+
"""
|
|
390
|
+
nxt = dict(marking)
|
|
391
|
+
for eid in reaction_ids:
|
|
392
|
+
tail, head = self.edges[eid]
|
|
393
|
+
for s, coeff in tail.items():
|
|
394
|
+
nxt[s] = nxt.get(s, 0) - int(coeff)
|
|
395
|
+
for s, coeff in head.items():
|
|
396
|
+
nxt[s] = nxt.get(s, 0) + int(coeff)
|
|
397
|
+
return self._normalize_marking(nxt)
|
|
398
|
+
|
|
399
|
+
@staticmethod
|
|
400
|
+
def _update_first_depth_maps(
|
|
401
|
+
*,
|
|
402
|
+
depth: int,
|
|
403
|
+
enabled_now: Iterable[str],
|
|
404
|
+
new_species: Set[str],
|
|
405
|
+
seen_reactions: Set[str],
|
|
406
|
+
reaction_first_depth: Dict[str, int],
|
|
407
|
+
species_first_depth: Dict[str, int],
|
|
408
|
+
) -> None:
|
|
409
|
+
"""
|
|
410
|
+
Update first-seen depth maps for reactions and species.
|
|
411
|
+
|
|
412
|
+
Reactions are assigned a first depth only once, when first observed as
|
|
413
|
+
enabled. Species are assigned a first depth when they first appear in
|
|
414
|
+
the newly reached set.
|
|
415
|
+
|
|
416
|
+
:param depth:
|
|
417
|
+
Current traversal depth.
|
|
418
|
+
:type depth: int
|
|
419
|
+
:param enabled_now:
|
|
420
|
+
Reactions enabled at the current depth.
|
|
421
|
+
:type enabled_now: Iterable[str]
|
|
422
|
+
:param new_species:
|
|
423
|
+
Species that became newly reachable at the current depth.
|
|
424
|
+
:type new_species: Set[str]
|
|
425
|
+
:param seen_reactions:
|
|
426
|
+
Mutable set of reactions already assigned a first depth.
|
|
427
|
+
:type seen_reactions: Set[str]
|
|
428
|
+
:param reaction_first_depth:
|
|
429
|
+
Output mapping from reaction identifier to first enabling depth.
|
|
430
|
+
:type reaction_first_depth: Dict[str, int]
|
|
431
|
+
:param species_first_depth:
|
|
432
|
+
Output mapping from species identifier to first reachability depth.
|
|
433
|
+
:type species_first_depth: Dict[str, int]
|
|
434
|
+
"""
|
|
435
|
+
for eid in enabled_now:
|
|
436
|
+
if eid not in seen_reactions:
|
|
437
|
+
reaction_first_depth[eid] = depth
|
|
438
|
+
seen_reactions.add(eid)
|
|
439
|
+
for s in sorted(new_species):
|
|
440
|
+
species_first_depth[s] = depth
|
|
441
|
+
|
|
442
|
+
# ------------------------------------------------------------------
|
|
443
|
+
# main computations
|
|
444
|
+
# ------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
def compute_layers_set(
|
|
447
|
+
self,
|
|
448
|
+
initial_species: Iterable[str],
|
|
449
|
+
max_layers: Optional[int] = None,
|
|
450
|
+
) -> ReachabilityResult:
|
|
451
|
+
"""Compute layered forward reachability in set semantics.
|
|
452
|
+
|
|
453
|
+
In this mode, only species presence matters. Stoichiometric
|
|
454
|
+
multiplicities are ignored when deciding whether a reaction is enabled.
|
|
455
|
+
|
|
456
|
+
The algorithm proceeds layer by layer:
|
|
457
|
+
1. find all reactions whose reactants are already reachable,
|
|
458
|
+
2. keep only reactions not previously seen as enabled,
|
|
459
|
+
3. collect all products they produce,
|
|
460
|
+
4. add any newly produced species to the reachable set.
|
|
461
|
+
|
|
462
|
+
:param initial_species:
|
|
463
|
+
Initial set of reachable species.
|
|
464
|
+
:type initial_species: Iterable[str]
|
|
465
|
+
:param max_layers:
|
|
466
|
+
Optional layer cap overriding the instance configuration.
|
|
467
|
+
:type max_layers: Optional[int]
|
|
468
|
+
:return:
|
|
469
|
+
Reachability result containing the full layered traversal trace.
|
|
470
|
+
:rtype: ReachabilityResult
|
|
471
|
+
|
|
472
|
+
.. rubric:: Example
|
|
473
|
+
|
|
474
|
+
.. code-block:: python
|
|
475
|
+
|
|
476
|
+
rr = PathwayReachability().load_hypergraph(
|
|
477
|
+
vertices=["A", "B", "C", "D"],
|
|
478
|
+
edges={
|
|
479
|
+
"r1": ({"A": 1, "B": 1}, {"C": 1}),
|
|
480
|
+
"r2": ({"C": 1}, {"D": 1}),
|
|
481
|
+
},
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
result = rr.compute_layers_set(initial_species=["A", "B"])
|
|
485
|
+
|
|
486
|
+
for layer in result.layers:
|
|
487
|
+
print(layer.depth, layer.newly_reachable_species)
|
|
488
|
+
|
|
489
|
+
# 1 ['C']
|
|
490
|
+
# 2 ['D']
|
|
491
|
+
"""
|
|
492
|
+
max_layers = max_layers if max_layers is not None else self._config.max_layers
|
|
493
|
+
reachable: Set[str] = {str(x) for x in initial_species}
|
|
494
|
+
seen_reactions: Set[str] = set()
|
|
495
|
+
|
|
496
|
+
species_first_depth: Dict[str, int] = {s: 0 for s in reachable}
|
|
497
|
+
reaction_first_depth: Dict[str, int] = {}
|
|
498
|
+
layers: List[ReachabilityLayer] = []
|
|
499
|
+
|
|
500
|
+
for depth in range(1, max_layers + 1):
|
|
501
|
+
newly_enabled = [
|
|
502
|
+
eid
|
|
503
|
+
for eid in self._enabled_reactions_for_set(reachable)
|
|
504
|
+
if eid not in seen_reactions
|
|
505
|
+
]
|
|
506
|
+
produced_this_round: Set[str] = set()
|
|
507
|
+
for eid in newly_enabled:
|
|
508
|
+
_, head = self.edges[eid]
|
|
509
|
+
produced_this_round.update(head.keys())
|
|
510
|
+
new_species = produced_this_round - reachable
|
|
511
|
+
|
|
512
|
+
if not newly_enabled and not new_species:
|
|
513
|
+
break
|
|
514
|
+
|
|
515
|
+
self._update_first_depth_maps(
|
|
516
|
+
depth=depth,
|
|
517
|
+
enabled_now=newly_enabled,
|
|
518
|
+
new_species=new_species,
|
|
519
|
+
seen_reactions=seen_reactions,
|
|
520
|
+
reaction_first_depth=reaction_first_depth,
|
|
521
|
+
species_first_depth=species_first_depth,
|
|
522
|
+
)
|
|
523
|
+
reachable.update(new_species)
|
|
524
|
+
|
|
525
|
+
layers.append(
|
|
526
|
+
ReachabilityLayer(
|
|
527
|
+
depth=depth,
|
|
528
|
+
newly_enabled_reactions=sorted(newly_enabled),
|
|
529
|
+
newly_reachable_species=sorted(new_species),
|
|
530
|
+
all_reachable_species=sorted(reachable),
|
|
531
|
+
)
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
if self._config.stop_when_no_new_species and not new_species:
|
|
535
|
+
break
|
|
536
|
+
|
|
537
|
+
return ReachabilityResult(
|
|
538
|
+
initial_species=sorted(set(str(x) for x in initial_species)),
|
|
539
|
+
layers=layers,
|
|
540
|
+
species_first_depth=species_first_depth,
|
|
541
|
+
reaction_first_depth=reaction_first_depth,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
def compute_layers_multiset(
|
|
545
|
+
self,
|
|
546
|
+
initial_marking: Mapping[str, int],
|
|
547
|
+
max_layers: Optional[int] = None,
|
|
548
|
+
) -> ReachabilityResult:
|
|
549
|
+
"""Compute layered forward reachability in multiset semantics.
|
|
550
|
+
|
|
551
|
+
In this mode, species counts matter. A reaction is enabled only if the
|
|
552
|
+
current marking contains enough multiplicity for every reactant. At
|
|
553
|
+
each layer, all currently enabled reactions are fired once in batch.
|
|
554
|
+
|
|
555
|
+
This procedure is useful when approximate token-flow behavior is
|
|
556
|
+
desired, but it should not be confused with exhaustive Petri-net
|
|
557
|
+
reachability analysis over all possible firing sequences.
|
|
558
|
+
|
|
559
|
+
:param initial_marking:
|
|
560
|
+
Initial species marking.
|
|
561
|
+
:type initial_marking: Mapping[str, int]
|
|
562
|
+
:param max_layers:
|
|
563
|
+
Optional layer cap overriding the instance configuration.
|
|
564
|
+
:type max_layers: Optional[int]
|
|
565
|
+
:return:
|
|
566
|
+
Reachability result containing the full layered traversal trace.
|
|
567
|
+
:rtype: ReachabilityResult
|
|
568
|
+
|
|
569
|
+
.. rubric:: Example
|
|
570
|
+
|
|
571
|
+
.. code-block:: python
|
|
572
|
+
|
|
573
|
+
rr = PathwayReachability().load_hypergraph(
|
|
574
|
+
vertices=["A", "B", "C"],
|
|
575
|
+
edges={
|
|
576
|
+
"r1": ({"A": 2}, {"B": 1}),
|
|
577
|
+
"r2": ({"B": 1}, {"C": 1}),
|
|
578
|
+
},
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
result = rr.compute_layers_multiset(initial_marking={"A": 2})
|
|
582
|
+
|
|
583
|
+
print(result.species_first_depth)
|
|
584
|
+
# {'A': 0, 'B': 1, 'C': 2}
|
|
585
|
+
"""
|
|
586
|
+
max_layers = max_layers if max_layers is not None else self._config.max_layers
|
|
587
|
+
marking = self._normalize_marking(initial_marking)
|
|
588
|
+
support = self._support(marking)
|
|
589
|
+
|
|
590
|
+
species_first_depth: Dict[str, int] = {s: 0 for s in support}
|
|
591
|
+
reaction_first_depth: Dict[str, int] = {}
|
|
592
|
+
layers: List[ReachabilityLayer] = []
|
|
593
|
+
seen_reactions: Set[str] = set()
|
|
594
|
+
|
|
595
|
+
for depth in range(1, max_layers + 1):
|
|
596
|
+
enabled_now = self._enabled_reactions_for_marking(marking)
|
|
597
|
+
if not enabled_now:
|
|
598
|
+
break
|
|
599
|
+
|
|
600
|
+
next_marking = self._fire_reactions_once_in_batch(marking, enabled_now)
|
|
601
|
+
next_support = self._support(next_marking)
|
|
602
|
+
new_species = next_support - support
|
|
603
|
+
|
|
604
|
+
self._update_first_depth_maps(
|
|
605
|
+
depth=depth,
|
|
606
|
+
enabled_now=enabled_now,
|
|
607
|
+
new_species=new_species,
|
|
608
|
+
seen_reactions=seen_reactions,
|
|
609
|
+
reaction_first_depth=reaction_first_depth,
|
|
610
|
+
species_first_depth=species_first_depth,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
layers.append(
|
|
614
|
+
ReachabilityLayer(
|
|
615
|
+
depth=depth,
|
|
616
|
+
newly_enabled_reactions=sorted(enabled_now),
|
|
617
|
+
newly_reachable_species=sorted(new_species),
|
|
618
|
+
all_reachable_species=sorted(next_support),
|
|
619
|
+
)
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
marking = next_marking
|
|
623
|
+
support = next_support
|
|
624
|
+
|
|
625
|
+
if self._config.stop_when_no_new_species and not new_species:
|
|
626
|
+
break
|
|
627
|
+
|
|
628
|
+
return ReachabilityResult(
|
|
629
|
+
initial_species=sorted(
|
|
630
|
+
self._support(self._normalize_marking(initial_marking))
|
|
631
|
+
),
|
|
632
|
+
layers=layers,
|
|
633
|
+
species_first_depth=species_first_depth,
|
|
634
|
+
reaction_first_depth=reaction_first_depth,
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
# ------------------------------------------------------------------
|
|
638
|
+
# export helpers
|
|
639
|
+
# ------------------------------------------------------------------
|
|
640
|
+
|
|
641
|
+
def export_layers_json(
|
|
642
|
+
self, result: ReachabilityResult, fn: str
|
|
643
|
+
) -> "PathwayReachability":
|
|
644
|
+
"""Export a reachability result to a JSON file.
|
|
645
|
+
|
|
646
|
+
The output contains the initial species set, first-depth maps, and the
|
|
647
|
+
full list of layered traversal records.
|
|
648
|
+
|
|
649
|
+
:param result:
|
|
650
|
+
Reachability result to serialize.
|
|
651
|
+
:type result: ReachabilityResult
|
|
652
|
+
:param fn:
|
|
653
|
+
Output JSON filename.
|
|
654
|
+
:type fn: str
|
|
655
|
+
:return:
|
|
656
|
+
The current instance, to allow fluent chaining.
|
|
657
|
+
:rtype: PathwayReachability
|
|
658
|
+
:raises OSError:
|
|
659
|
+
Raised if the target file cannot be written.
|
|
660
|
+
|
|
661
|
+
.. rubric:: Example
|
|
662
|
+
|
|
663
|
+
.. code-block:: python
|
|
664
|
+
|
|
665
|
+
rr.export_layers_json(result, "reachability_layers.json")
|
|
666
|
+
"""
|
|
667
|
+
data = {
|
|
668
|
+
"initial_species": result.initial_species,
|
|
669
|
+
"species_first_depth": result.species_first_depth,
|
|
670
|
+
"reaction_first_depth": result.reaction_first_depth,
|
|
671
|
+
"layers": [
|
|
672
|
+
{
|
|
673
|
+
"depth": layer.depth,
|
|
674
|
+
"newly_enabled_reactions": layer.newly_enabled_reactions,
|
|
675
|
+
"newly_reachable_species": layer.newly_reachable_species,
|
|
676
|
+
"all_reachable_species": layer.all_reachable_species,
|
|
677
|
+
}
|
|
678
|
+
for layer in result.layers
|
|
679
|
+
],
|
|
680
|
+
}
|
|
681
|
+
with open(fn, "w") as fh:
|
|
682
|
+
json.dump(data, fh, indent=2)
|
|
683
|
+
return self
|
|
684
|
+
|
|
685
|
+
def __repr__(self) -> str: # pragma: no cover - simple repr
|
|
686
|
+
"""Return a compact developer-friendly representation.
|
|
687
|
+
|
|
688
|
+
:return:
|
|
689
|
+
String representation including the number of vertices and edges.
|
|
690
|
+
:rtype: str
|
|
691
|
+
"""
|
|
692
|
+
return f"<PathwayReachability vertices={len(self.vertices)} edges={len(self.edges)}>"
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def syncrn_to_reachability_inputs(
|
|
696
|
+
crn: object,
|
|
697
|
+
*,
|
|
698
|
+
species: str = "label",
|
|
699
|
+
reaction: str = "id",
|
|
700
|
+
) -> Tuple[List[str], Dict[str, Tuple[Dict[str, int], Dict[str, int]]]]:
|
|
701
|
+
"""Convert a SynCRN-like object into tokenized reachability inputs.
|
|
702
|
+
|
|
703
|
+
This is a lightweight adapter returning only the tokenized species list and
|
|
704
|
+
reaction hyperedges needed by :class:`PathwayReachability`.
|
|
705
|
+
|
|
706
|
+
:param crn:
|
|
707
|
+
SynCRN-like object to tokenize.
|
|
708
|
+
:type crn: object
|
|
709
|
+
:param species:
|
|
710
|
+
Species attribute used during tokenization.
|
|
711
|
+
:type species: str
|
|
712
|
+
:param reaction:
|
|
713
|
+
Reaction attribute used during tokenization.
|
|
714
|
+
:type reaction: str
|
|
715
|
+
:return:
|
|
716
|
+
Pair ``(vertices, edges)`` where ``vertices`` is the species token list
|
|
717
|
+
and ``edges`` maps reaction token to ``(tail_multiset, head_multiset)``.
|
|
718
|
+
:rtype: Tuple[List[str], Dict[str, Tuple[Dict[str, int], Dict[str, int]]]]
|
|
719
|
+
|
|
720
|
+
.. rubric:: Example
|
|
721
|
+
|
|
722
|
+
.. code-block:: python
|
|
723
|
+
|
|
724
|
+
vertices, edges = syncrn_to_reachability_inputs(
|
|
725
|
+
syncrn_object,
|
|
726
|
+
species="label",
|
|
727
|
+
reaction="id",
|
|
728
|
+
)
|
|
729
|
+
"""
|
|
730
|
+
vertices, edges, _, _, _ = tokenize_syncrn_incidence(
|
|
731
|
+
crn, species=species, reaction=reaction
|
|
732
|
+
)
|
|
733
|
+
return vertices, edges
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
def run_reachability_from_syncrn(
|
|
737
|
+
crn: object,
|
|
738
|
+
initial_species: Iterable[str],
|
|
739
|
+
*,
|
|
740
|
+
species: str = "label",
|
|
741
|
+
reaction: str = "id",
|
|
742
|
+
verbose: bool = True,
|
|
743
|
+
) -> Tuple[PathwayReachability, ReachabilityResult]:
|
|
744
|
+
"""Run layered qualitative reachability directly from a SynCRN-like object.
|
|
745
|
+
|
|
746
|
+
This convenience function constructs a :class:`PathwayReachability`
|
|
747
|
+
instance, loads the tokenized SynCRN representation, computes set-based
|
|
748
|
+
layered reachability, and optionally prints a human-readable traversal
|
|
749
|
+
summary.
|
|
750
|
+
|
|
751
|
+
:param crn:
|
|
752
|
+
SynCRN-like object to analyze.
|
|
753
|
+
:type crn: object
|
|
754
|
+
:param initial_species:
|
|
755
|
+
Initial reachable species set.
|
|
756
|
+
:type initial_species: Iterable[str]
|
|
757
|
+
:param species:
|
|
758
|
+
Species attribute used during tokenization.
|
|
759
|
+
:type species: str
|
|
760
|
+
:param reaction:
|
|
761
|
+
Reaction attribute used during tokenization.
|
|
762
|
+
:type reaction: str
|
|
763
|
+
:param verbose:
|
|
764
|
+
If ``True``, print the tokenized edges and each traversal layer.
|
|
765
|
+
:type verbose: bool
|
|
766
|
+
:return:
|
|
767
|
+
Pair ``(reachability_engine, result)``.
|
|
768
|
+
:rtype: Tuple[PathwayReachability, ReachabilityResult]
|
|
769
|
+
|
|
770
|
+
.. rubric:: Example
|
|
771
|
+
|
|
772
|
+
.. code-block:: python
|
|
773
|
+
|
|
774
|
+
rr, result = run_reachability_from_syncrn(
|
|
775
|
+
syncrn_object,
|
|
776
|
+
initial_species=["A", "B"],
|
|
777
|
+
species="label",
|
|
778
|
+
reaction="id",
|
|
779
|
+
verbose=True,
|
|
780
|
+
)
|
|
781
|
+
"""
|
|
782
|
+
rr = PathwayReachability().load_syncrn(crn, species=species, reaction=reaction)
|
|
783
|
+
result = rr.compute_layers_set(initial_species=initial_species)
|
|
784
|
+
|
|
785
|
+
if verbose:
|
|
786
|
+
print("Edges:")
|
|
787
|
+
for eid, (t, h) in rr.edges.items():
|
|
788
|
+
print(f" {eid}: {t} >> {h}")
|
|
789
|
+
print("Initial species:", sorted(set(str(x) for x in initial_species)))
|
|
790
|
+
for layer in result.layers:
|
|
791
|
+
print(f"Layer {layer.depth}:")
|
|
792
|
+
print(" Newly enabled reactions:", layer.newly_enabled_reactions)
|
|
793
|
+
print(" Newly reachable species:", layer.newly_reachable_species)
|
|
794
|
+
print(" All reachable species:", layer.all_reachable_species)
|
|
795
|
+
|
|
796
|
+
return rr, result
|