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,832 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
import networkx as nx
|
|
7
|
+
import numpy as np
|
|
8
|
+
from networkx.algorithms import bipartite as nx_bipartite
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import sympy as sp # type: ignore
|
|
12
|
+
|
|
13
|
+
_SYMPY_AVAILABLE = True
|
|
14
|
+
except Exception:
|
|
15
|
+
sp = None # type: ignore
|
|
16
|
+
_SYMPY_AVAILABLE = False
|
|
17
|
+
|
|
18
|
+
from .stoich import build_S, build_S_minus_plus
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"StructuralSingularitySummary",
|
|
22
|
+
"symbolic_reactivity_matrix",
|
|
23
|
+
"symbolic_jacobian",
|
|
24
|
+
"jacobian_sparsity",
|
|
25
|
+
"jacobian_sign_pattern",
|
|
26
|
+
"species_influence_graph",
|
|
27
|
+
"structural_singularity_summary",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _require_sympy() -> None:
|
|
32
|
+
"""Ensure that SymPy is available before using symbolic CRN dynamics helpers.
|
|
33
|
+
|
|
34
|
+
This helper centralizes the optional dependency check used by symbolic
|
|
35
|
+
routines such as symbolic Jacobian construction and exact determinant
|
|
36
|
+
evaluation.
|
|
37
|
+
|
|
38
|
+
:raises ImportError:
|
|
39
|
+
If :mod:`sympy` is not installed or could not be imported.
|
|
40
|
+
|
|
41
|
+
.. rubric:: Example
|
|
42
|
+
|
|
43
|
+
.. code-block:: python
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
_require_sympy()
|
|
47
|
+
except ImportError as exc:
|
|
48
|
+
print(exc)
|
|
49
|
+
"""
|
|
50
|
+
if not _SYMPY_AVAILABLE or sp is None:
|
|
51
|
+
raise ImportError(
|
|
52
|
+
"sympy is required for symbolic CRN dynamics helpers. "
|
|
53
|
+
"Please install it, e.g. `pip install sympy`."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _safe_symbol_token(value: Any) -> str:
|
|
58
|
+
"""Convert an arbitrary object into a SymPy-safe symbol token.
|
|
59
|
+
|
|
60
|
+
Non-alphanumeric characters are replaced by underscores. If the resulting
|
|
61
|
+
token is empty, ``"x"`` is used. If the token starts with a digit, a
|
|
62
|
+
leading underscore is prepended.
|
|
63
|
+
|
|
64
|
+
:param value:
|
|
65
|
+
Arbitrary object to convert into a symbol-safe token.
|
|
66
|
+
:type value:
|
|
67
|
+
Any
|
|
68
|
+
|
|
69
|
+
:return:
|
|
70
|
+
Sanitized token suitable for building SymPy symbol names.
|
|
71
|
+
:rtype:
|
|
72
|
+
str
|
|
73
|
+
|
|
74
|
+
.. rubric:: Example
|
|
75
|
+
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
|
|
78
|
+
_safe_symbol_token("A")
|
|
79
|
+
_safe_symbol_token("species-1")
|
|
80
|
+
_safe_symbol_token("3-node")
|
|
81
|
+
|
|
82
|
+
# possible outputs:
|
|
83
|
+
# "A"
|
|
84
|
+
# "species_1"
|
|
85
|
+
# "_3_node"
|
|
86
|
+
"""
|
|
87
|
+
text = str(value)
|
|
88
|
+
token = "".join(ch if ch.isalnum() else "_" for ch in text)
|
|
89
|
+
if not token:
|
|
90
|
+
token = "x"
|
|
91
|
+
if token[0].isdigit():
|
|
92
|
+
token = f"_{token}"
|
|
93
|
+
return token
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _sympy_matrix_from_numpy(A: np.ndarray) -> "sp.Matrix":
|
|
97
|
+
"""Convert a numeric NumPy array into a SymPy matrix.
|
|
98
|
+
|
|
99
|
+
Each entry is passed through :func:`sympy.nsimplify` so that integer and
|
|
100
|
+
rational values are preserved when possible.
|
|
101
|
+
|
|
102
|
+
:param A:
|
|
103
|
+
Numeric array to convert.
|
|
104
|
+
:type A:
|
|
105
|
+
numpy.ndarray
|
|
106
|
+
|
|
107
|
+
:return:
|
|
108
|
+
SymPy matrix with simplified symbolic entries.
|
|
109
|
+
:rtype:
|
|
110
|
+
sp.Matrix
|
|
111
|
+
|
|
112
|
+
:raises ImportError:
|
|
113
|
+
If :mod:`sympy` is not available.
|
|
114
|
+
|
|
115
|
+
.. rubric:: Example
|
|
116
|
+
|
|
117
|
+
.. code-block:: python
|
|
118
|
+
|
|
119
|
+
import numpy as np
|
|
120
|
+
|
|
121
|
+
A = np.array([[1.0, -1.0], [0.0, 2.0]])
|
|
122
|
+
M = _sympy_matrix_from_numpy(A)
|
|
123
|
+
print(M)
|
|
124
|
+
"""
|
|
125
|
+
_require_sympy()
|
|
126
|
+
return sp.Matrix([[sp.nsimplify(x) for x in row] for row in np.asarray(A)])
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _structural_sign_pattern(
|
|
130
|
+
S: np.ndarray,
|
|
131
|
+
S_minus: np.ndarray,
|
|
132
|
+
*,
|
|
133
|
+
tol: float = 1e-12,
|
|
134
|
+
) -> np.ndarray:
|
|
135
|
+
"""Compute the structural sign pattern of the symbolic Jacobian.
|
|
136
|
+
|
|
137
|
+
For a symbolic Jacobian of the form ``G = S R``, the sign of entry
|
|
138
|
+
``G[i, k]`` depends on the stoichiometric effects in row ``i`` and on
|
|
139
|
+
whether species ``k`` participates as a reactant in the corresponding
|
|
140
|
+
rule columns. This routine derives the sign pattern directly from the
|
|
141
|
+
stoichiometric matrices without constructing the full symbolic Jacobian.
|
|
142
|
+
|
|
143
|
+
Returned entries are one of:
|
|
144
|
+
|
|
145
|
+
- ``"0"``
|
|
146
|
+
- ``"+"``
|
|
147
|
+
- ``"-"``
|
|
148
|
+
- ``"mixed"``
|
|
149
|
+
|
|
150
|
+
:param S:
|
|
151
|
+
Net stoichiometric matrix with shape ``(n_species, n_rules)``.
|
|
152
|
+
:type S:
|
|
153
|
+
numpy.ndarray
|
|
154
|
+
:param S_minus:
|
|
155
|
+
Reactant stoichiometric matrix with shape ``(n_species, n_rules)``.
|
|
156
|
+
:type S_minus:
|
|
157
|
+
numpy.ndarray
|
|
158
|
+
:param tol:
|
|
159
|
+
Numerical tolerance used to test structural positivity or negativity.
|
|
160
|
+
:type tol:
|
|
161
|
+
float
|
|
162
|
+
|
|
163
|
+
:return:
|
|
164
|
+
Matrix of sign labels with shape ``(n_species, n_species)``.
|
|
165
|
+
:rtype:
|
|
166
|
+
numpy.ndarray
|
|
167
|
+
|
|
168
|
+
.. rubric:: Example
|
|
169
|
+
|
|
170
|
+
.. code-block:: python
|
|
171
|
+
|
|
172
|
+
import numpy as np
|
|
173
|
+
|
|
174
|
+
S = np.array([
|
|
175
|
+
[-1, 0],
|
|
176
|
+
[ 1, -1],
|
|
177
|
+
[ 0, 1],
|
|
178
|
+
], dtype=float)
|
|
179
|
+
|
|
180
|
+
S_minus = np.array([
|
|
181
|
+
[1, 0],
|
|
182
|
+
[0, 1],
|
|
183
|
+
[0, 0],
|
|
184
|
+
], dtype=float)
|
|
185
|
+
|
|
186
|
+
P = _structural_sign_pattern(S, S_minus)
|
|
187
|
+
print(P)
|
|
188
|
+
"""
|
|
189
|
+
n_species, n_rules = S.shape
|
|
190
|
+
out = np.full((n_species, n_species), "0", dtype=object)
|
|
191
|
+
|
|
192
|
+
for i in range(n_species): # affected species / row of G
|
|
193
|
+
for k in range(n_species): # source species / column of G
|
|
194
|
+
has_pos = False
|
|
195
|
+
has_neg = False
|
|
196
|
+
|
|
197
|
+
for j in range(n_rules):
|
|
198
|
+
if float(S_minus[k, j]) > tol:
|
|
199
|
+
coeff = float(S[i, j])
|
|
200
|
+
if coeff > tol:
|
|
201
|
+
has_pos = True
|
|
202
|
+
elif coeff < -tol:
|
|
203
|
+
has_neg = True
|
|
204
|
+
|
|
205
|
+
if has_pos and has_neg:
|
|
206
|
+
out[i, k] = "mixed"
|
|
207
|
+
break
|
|
208
|
+
else:
|
|
209
|
+
if has_pos and has_neg:
|
|
210
|
+
out[i, k] = "mixed"
|
|
211
|
+
elif has_pos:
|
|
212
|
+
out[i, k] = "+"
|
|
213
|
+
elif has_neg:
|
|
214
|
+
out[i, k] = "-"
|
|
215
|
+
else:
|
|
216
|
+
out[i, k] = "0"
|
|
217
|
+
|
|
218
|
+
return out
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _jacobian_pattern_bipartite(A: np.ndarray) -> Tuple[nx.Graph, List[str], List[str]]:
|
|
222
|
+
"""Build the row/column bipartite graph for a Jacobian sparsity pattern.
|
|
223
|
+
|
|
224
|
+
The bipartite graph is used for structural-rank diagnostics via maximum
|
|
225
|
+
matching. Row nodes correspond to Jacobian rows and column nodes correspond
|
|
226
|
+
to Jacobian columns. An edge is added when the corresponding entry in the
|
|
227
|
+
sparsity matrix is nonzero.
|
|
228
|
+
|
|
229
|
+
:param A:
|
|
230
|
+
Boolean or truthy/falsy Jacobian sparsity matrix.
|
|
231
|
+
:type A:
|
|
232
|
+
numpy.ndarray
|
|
233
|
+
|
|
234
|
+
:return:
|
|
235
|
+
Tuple containing:
|
|
236
|
+
|
|
237
|
+
- bipartite graph,
|
|
238
|
+
- ordered row-node names,
|
|
239
|
+
- ordered column-node names.
|
|
240
|
+
:rtype:
|
|
241
|
+
Tuple[nx.Graph, List[str], List[str]]
|
|
242
|
+
|
|
243
|
+
.. rubric:: Example
|
|
244
|
+
|
|
245
|
+
.. code-block:: python
|
|
246
|
+
|
|
247
|
+
import numpy as np
|
|
248
|
+
|
|
249
|
+
A = np.array([
|
|
250
|
+
[True, False],
|
|
251
|
+
[True, True],
|
|
252
|
+
], dtype=bool)
|
|
253
|
+
|
|
254
|
+
B, row_nodes, col_nodes = _jacobian_pattern_bipartite(A)
|
|
255
|
+
print(row_nodes)
|
|
256
|
+
print(col_nodes)
|
|
257
|
+
print(B.number_of_edges())
|
|
258
|
+
"""
|
|
259
|
+
n_rows, n_cols = A.shape
|
|
260
|
+
B = nx.Graph()
|
|
261
|
+
|
|
262
|
+
row_nodes = [f"row:{i}" for i in range(n_rows)]
|
|
263
|
+
col_nodes = [f"col:{j}" for j in range(n_cols)]
|
|
264
|
+
|
|
265
|
+
B.add_nodes_from(row_nodes, bipartite=0)
|
|
266
|
+
B.add_nodes_from(col_nodes, bipartite=1)
|
|
267
|
+
|
|
268
|
+
for i in range(n_rows):
|
|
269
|
+
for j in range(n_cols):
|
|
270
|
+
if bool(A[i, j]):
|
|
271
|
+
B.add_edge(row_nodes[i], col_nodes[j])
|
|
272
|
+
|
|
273
|
+
return B, row_nodes, col_nodes
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@dataclass
|
|
277
|
+
class StructuralSingularitySummary:
|
|
278
|
+
"""Summary of structural singularity diagnostics for a symbolic Jacobian.
|
|
279
|
+
|
|
280
|
+
The diagnostics are species-level because the Jacobian is a
|
|
281
|
+
species-by-species object, even though the underlying SynCRN graph is
|
|
282
|
+
bipartite with species nodes and rule nodes.
|
|
283
|
+
|
|
284
|
+
:param n_species:
|
|
285
|
+
Number of species, i.e. Jacobian dimension.
|
|
286
|
+
:type n_species:
|
|
287
|
+
int
|
|
288
|
+
:param structural_rank:
|
|
289
|
+
Structural rank inferred from the Jacobian sparsity pattern.
|
|
290
|
+
:type structural_rank:
|
|
291
|
+
int
|
|
292
|
+
:param has_perfect_matching:
|
|
293
|
+
Whether the bipartite sparsity graph admits a perfect matching.
|
|
294
|
+
:type has_perfect_matching:
|
|
295
|
+
bool
|
|
296
|
+
:param pattern_singular:
|
|
297
|
+
Whether the Jacobian is singular at the structural-pattern level.
|
|
298
|
+
:type pattern_singular:
|
|
299
|
+
bool
|
|
300
|
+
:param determinant_checked:
|
|
301
|
+
Whether an exact symbolic determinant was computed.
|
|
302
|
+
:type determinant_checked:
|
|
303
|
+
bool
|
|
304
|
+
:param determinant_expr:
|
|
305
|
+
Exact symbolic determinant expression, if evaluated.
|
|
306
|
+
:type determinant_expr:
|
|
307
|
+
Optional[Any]
|
|
308
|
+
:param determinant_is_zero:
|
|
309
|
+
Whether the exact symbolic determinant simplified to zero.
|
|
310
|
+
:type determinant_is_zero:
|
|
311
|
+
Optional[bool]
|
|
312
|
+
|
|
313
|
+
.. rubric:: Example
|
|
314
|
+
|
|
315
|
+
.. code-block:: python
|
|
316
|
+
|
|
317
|
+
summary = StructuralSingularitySummary(
|
|
318
|
+
n_species=3,
|
|
319
|
+
structural_rank=2,
|
|
320
|
+
has_perfect_matching=False,
|
|
321
|
+
pattern_singular=True,
|
|
322
|
+
determinant_checked=False,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
print(summary.classification)
|
|
326
|
+
print(summary.to_dict())
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
n_species: int
|
|
330
|
+
structural_rank: int
|
|
331
|
+
has_perfect_matching: bool
|
|
332
|
+
pattern_singular: bool
|
|
333
|
+
determinant_checked: bool
|
|
334
|
+
determinant_expr: Optional[Any] = None
|
|
335
|
+
determinant_is_zero: Optional[bool] = None
|
|
336
|
+
|
|
337
|
+
@property
|
|
338
|
+
def classification(self) -> str:
|
|
339
|
+
"""Return a concise structural-singularity classification label.
|
|
340
|
+
|
|
341
|
+
Possible values include pattern-level singularity, exact symbolic
|
|
342
|
+
singularity, exact symbolic nonsingularity, or the case where only
|
|
343
|
+
the sparsity-pattern analysis was performed.
|
|
344
|
+
|
|
345
|
+
:return:
|
|
346
|
+
Classification string summarizing the diagnostic outcome.
|
|
347
|
+
:rtype:
|
|
348
|
+
str
|
|
349
|
+
|
|
350
|
+
.. rubric:: Example
|
|
351
|
+
|
|
352
|
+
.. code-block:: python
|
|
353
|
+
|
|
354
|
+
summary = StructuralSingularitySummary(
|
|
355
|
+
n_species=4,
|
|
356
|
+
structural_rank=4,
|
|
357
|
+
has_perfect_matching=True,
|
|
358
|
+
pattern_singular=False,
|
|
359
|
+
determinant_checked=True,
|
|
360
|
+
determinant_is_zero=False,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
print(summary.classification)
|
|
364
|
+
"""
|
|
365
|
+
if self.pattern_singular:
|
|
366
|
+
return "singular_by_pattern"
|
|
367
|
+
if self.determinant_is_zero is True:
|
|
368
|
+
return "singular_by_exact_determinant"
|
|
369
|
+
if self.determinant_is_zero is False:
|
|
370
|
+
return "structurally_nonsingular"
|
|
371
|
+
return "pattern_nonsingular_exact_unchecked"
|
|
372
|
+
|
|
373
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
374
|
+
"""Convert the summary to a plain dictionary.
|
|
375
|
+
|
|
376
|
+
Symbolic determinant expressions are stringified so the result is easier
|
|
377
|
+
to serialize or log.
|
|
378
|
+
|
|
379
|
+
:return:
|
|
380
|
+
Plain dictionary representation of the summary.
|
|
381
|
+
:rtype:
|
|
382
|
+
Dict[str, Any]
|
|
383
|
+
|
|
384
|
+
.. rubric:: Example
|
|
385
|
+
|
|
386
|
+
.. code-block:: python
|
|
387
|
+
|
|
388
|
+
d = summary.to_dict()
|
|
389
|
+
print(d["classification"])
|
|
390
|
+
"""
|
|
391
|
+
return {
|
|
392
|
+
"n_species": self.n_species,
|
|
393
|
+
"structural_rank": self.structural_rank,
|
|
394
|
+
"has_perfect_matching": self.has_perfect_matching,
|
|
395
|
+
"pattern_singular": self.pattern_singular,
|
|
396
|
+
"determinant_checked": self.determinant_checked,
|
|
397
|
+
"determinant_expr": (
|
|
398
|
+
None if self.determinant_expr is None else str(self.determinant_expr)
|
|
399
|
+
),
|
|
400
|
+
"determinant_is_zero": self.determinant_is_zero,
|
|
401
|
+
"classification": self.classification,
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
def __str__(self) -> str:
|
|
405
|
+
"""Return a readable multiline summary.
|
|
406
|
+
|
|
407
|
+
:return:
|
|
408
|
+
Human-readable diagnostic summary.
|
|
409
|
+
:rtype:
|
|
410
|
+
str
|
|
411
|
+
|
|
412
|
+
.. rubric:: Example
|
|
413
|
+
|
|
414
|
+
.. code-block:: python
|
|
415
|
+
|
|
416
|
+
print(summary)
|
|
417
|
+
"""
|
|
418
|
+
lines = [
|
|
419
|
+
"StructuralSingularitySummary(",
|
|
420
|
+
f" n_species = {self.n_species}",
|
|
421
|
+
f" structural_rank = {self.structural_rank}",
|
|
422
|
+
f" has_perfect_matching = {self.has_perfect_matching}",
|
|
423
|
+
f" pattern_singular = {self.pattern_singular}",
|
|
424
|
+
f" determinant_checked = {self.determinant_checked}",
|
|
425
|
+
f" determinant_is_zero = {self.determinant_is_zero}",
|
|
426
|
+
f" classification = {self.classification}",
|
|
427
|
+
")",
|
|
428
|
+
]
|
|
429
|
+
return "\n".join(lines)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def symbolic_reactivity_matrix(
|
|
433
|
+
crn: Any,
|
|
434
|
+
*,
|
|
435
|
+
symbol_prefix: str = "rprime",
|
|
436
|
+
tol: float = 1e-12,
|
|
437
|
+
) -> Tuple[List[Any], List[Any], "sp.Matrix"]:
|
|
438
|
+
"""Build the symbolic reactivity matrix ``R``.
|
|
439
|
+
|
|
440
|
+
For a SynCRN with ``n_species`` species nodes and ``n_rules`` rule nodes,
|
|
441
|
+
the matrix ``R`` has shape ``(n_rules, n_species)``. Entry ``R[j, i]`` is
|
|
442
|
+
a positive symbolic variable if species ``i`` is a reactant of rule ``j``,
|
|
443
|
+
and zero otherwise.
|
|
444
|
+
|
|
445
|
+
This matrix captures the structural dependence of reaction rates on species
|
|
446
|
+
concentrations without assuming a specific kinetic law beyond positive
|
|
447
|
+
reactant sensitivity.
|
|
448
|
+
|
|
449
|
+
:param crn:
|
|
450
|
+
SynCRN graph or SynCRN-like object in the species/rule representation.
|
|
451
|
+
:type crn:
|
|
452
|
+
Any
|
|
453
|
+
:param symbol_prefix:
|
|
454
|
+
Prefix used when naming symbolic reactivity variables.
|
|
455
|
+
:type symbol_prefix:
|
|
456
|
+
str
|
|
457
|
+
:param tol:
|
|
458
|
+
Tolerance used to decide whether a reactant stoichiometric entry is
|
|
459
|
+
structurally positive.
|
|
460
|
+
:type tol:
|
|
461
|
+
float
|
|
462
|
+
|
|
463
|
+
:return:
|
|
464
|
+
Tuple containing:
|
|
465
|
+
|
|
466
|
+
- species node order,
|
|
467
|
+
- rule node order,
|
|
468
|
+
- symbolic reactivity matrix ``R``.
|
|
469
|
+
:rtype:
|
|
470
|
+
Tuple[List[Any], List[Any], sp.Matrix]
|
|
471
|
+
|
|
472
|
+
:raises ImportError:
|
|
473
|
+
If :mod:`sympy` is not available.
|
|
474
|
+
|
|
475
|
+
.. rubric:: Example
|
|
476
|
+
|
|
477
|
+
.. code-block:: python
|
|
478
|
+
|
|
479
|
+
species_order, rule_order, R = symbolic_reactivity_matrix(crn)
|
|
480
|
+
print(species_order)
|
|
481
|
+
print(rule_order)
|
|
482
|
+
print(R)
|
|
483
|
+
"""
|
|
484
|
+
_require_sympy()
|
|
485
|
+
|
|
486
|
+
species_order, rule_order, S_minus, _S_plus = build_S_minus_plus(crn)
|
|
487
|
+
n_species = len(species_order)
|
|
488
|
+
n_rules = len(rule_order)
|
|
489
|
+
|
|
490
|
+
rows = []
|
|
491
|
+
for j in range(n_rules):
|
|
492
|
+
r_tok = _safe_symbol_token(rule_order[j])
|
|
493
|
+
row = []
|
|
494
|
+
for i in range(n_species):
|
|
495
|
+
s_tok = _safe_symbol_token(species_order[i])
|
|
496
|
+
if float(S_minus[i, j]) > tol:
|
|
497
|
+
row.append(sp.Symbol(f"{symbol_prefix}_{r_tok}_{s_tok}", positive=True))
|
|
498
|
+
else:
|
|
499
|
+
row.append(sp.Integer(0))
|
|
500
|
+
rows.append(row)
|
|
501
|
+
|
|
502
|
+
R = sp.Matrix(rows)
|
|
503
|
+
return species_order, rule_order, R
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def symbolic_jacobian(
|
|
507
|
+
crn: Any,
|
|
508
|
+
*,
|
|
509
|
+
symbol_prefix: str = "rprime",
|
|
510
|
+
tol: float = 1e-12,
|
|
511
|
+
) -> Tuple[List[Any], List[Any], "sp.Matrix"]:
|
|
512
|
+
"""Build the symbolic Jacobian ``G = S R``.
|
|
513
|
+
|
|
514
|
+
Here ``S`` is the species-by-rule stoichiometric matrix and ``R`` is the
|
|
515
|
+
rule-by-species symbolic reactivity matrix induced by reactant incidence.
|
|
516
|
+
The resulting Jacobian is a species-by-species symbolic matrix describing
|
|
517
|
+
local structural influence in the CRN dynamics.
|
|
518
|
+
|
|
519
|
+
:param crn:
|
|
520
|
+
SynCRN graph or SynCRN-like object in the species/rule representation.
|
|
521
|
+
:type crn:
|
|
522
|
+
Any
|
|
523
|
+
:param symbol_prefix:
|
|
524
|
+
Prefix used for symbolic reactivity variables.
|
|
525
|
+
:type symbol_prefix:
|
|
526
|
+
str
|
|
527
|
+
:param tol:
|
|
528
|
+
Tolerance forwarded to :func:`symbolic_reactivity_matrix`.
|
|
529
|
+
:type tol:
|
|
530
|
+
float
|
|
531
|
+
|
|
532
|
+
:return:
|
|
533
|
+
Tuple containing:
|
|
534
|
+
|
|
535
|
+
- species node order,
|
|
536
|
+
- rule node order,
|
|
537
|
+
- symbolic Jacobian ``G``.
|
|
538
|
+
:rtype:
|
|
539
|
+
Tuple[List[Any], List[Any], sp.Matrix]
|
|
540
|
+
|
|
541
|
+
:raises ImportError:
|
|
542
|
+
If :mod:`sympy` is not available.
|
|
543
|
+
|
|
544
|
+
.. rubric:: Example
|
|
545
|
+
|
|
546
|
+
.. code-block:: python
|
|
547
|
+
|
|
548
|
+
species_order, rule_order, G = symbolic_jacobian(crn)
|
|
549
|
+
print(G.shape)
|
|
550
|
+
print(G)
|
|
551
|
+
"""
|
|
552
|
+
_require_sympy()
|
|
553
|
+
|
|
554
|
+
species_order, rule_order, S = build_S(crn)
|
|
555
|
+
_sp_order, _rule_order, R = symbolic_reactivity_matrix(
|
|
556
|
+
crn,
|
|
557
|
+
symbol_prefix=symbol_prefix,
|
|
558
|
+
tol=tol,
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
S_sym = _sympy_matrix_from_numpy(S)
|
|
562
|
+
G = S_sym * R
|
|
563
|
+
return species_order, rule_order, G
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def jacobian_sparsity(
|
|
567
|
+
crn: Any,
|
|
568
|
+
*,
|
|
569
|
+
tol: float = 1e-12,
|
|
570
|
+
) -> Tuple[List[Any], np.ndarray]:
|
|
571
|
+
"""Return the boolean sparsity pattern of the symbolic Jacobian.
|
|
572
|
+
|
|
573
|
+
Entry ``A[i, k]`` is ``True`` if species ``k`` can structurally influence
|
|
574
|
+
species ``i`` through at least one rule under the local linearized
|
|
575
|
+
dynamics.
|
|
576
|
+
|
|
577
|
+
:param crn:
|
|
578
|
+
SynCRN graph or SynCRN-like object in the species/rule representation.
|
|
579
|
+
:type crn:
|
|
580
|
+
Any
|
|
581
|
+
:param tol:
|
|
582
|
+
Tolerance used for structural zero testing.
|
|
583
|
+
:type tol:
|
|
584
|
+
float
|
|
585
|
+
|
|
586
|
+
:return:
|
|
587
|
+
Tuple containing:
|
|
588
|
+
|
|
589
|
+
- species node order,
|
|
590
|
+
- boolean Jacobian sparsity matrix.
|
|
591
|
+
:rtype:
|
|
592
|
+
Tuple[List[Any], numpy.ndarray]
|
|
593
|
+
|
|
594
|
+
.. rubric:: Example
|
|
595
|
+
|
|
596
|
+
.. code-block:: python
|
|
597
|
+
|
|
598
|
+
species_order, A = jacobian_sparsity(crn)
|
|
599
|
+
print(species_order)
|
|
600
|
+
print(A.astype(int))
|
|
601
|
+
"""
|
|
602
|
+
species_order, _rule_order, S_minus, S_plus = build_S_minus_plus(crn)
|
|
603
|
+
S = S_plus - S_minus
|
|
604
|
+
sign_pat = _structural_sign_pattern(S, S_minus, tol=tol)
|
|
605
|
+
A = sign_pat != "0"
|
|
606
|
+
return species_order, A
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def jacobian_sign_pattern(
|
|
610
|
+
crn: Any,
|
|
611
|
+
*,
|
|
612
|
+
tol: float = 1e-12,
|
|
613
|
+
) -> Tuple[List[Any], np.ndarray]:
|
|
614
|
+
"""Compute the structural sign pattern of the symbolic Jacobian.
|
|
615
|
+
|
|
616
|
+
Returned entries are one of:
|
|
617
|
+
|
|
618
|
+
- ``"0"``
|
|
619
|
+
- ``"+"``
|
|
620
|
+
- ``"-"``
|
|
621
|
+
- ``"mixed"``
|
|
622
|
+
|
|
623
|
+
The value ``"mixed"`` means that multiple structurally valid paths exist
|
|
624
|
+
with conflicting positive and negative net effects.
|
|
625
|
+
|
|
626
|
+
:param crn:
|
|
627
|
+
SynCRN graph or SynCRN-like object in the species/rule representation.
|
|
628
|
+
:type crn:
|
|
629
|
+
Any
|
|
630
|
+
:param tol:
|
|
631
|
+
Tolerance used for structural zero testing.
|
|
632
|
+
:type tol:
|
|
633
|
+
float
|
|
634
|
+
|
|
635
|
+
:return:
|
|
636
|
+
Tuple containing:
|
|
637
|
+
|
|
638
|
+
- species node order,
|
|
639
|
+
- Jacobian sign-pattern matrix.
|
|
640
|
+
:rtype:
|
|
641
|
+
Tuple[List[Any], numpy.ndarray]
|
|
642
|
+
|
|
643
|
+
.. rubric:: Example
|
|
644
|
+
|
|
645
|
+
.. code-block:: python
|
|
646
|
+
|
|
647
|
+
species_order, P = jacobian_sign_pattern(crn)
|
|
648
|
+
print(species_order)
|
|
649
|
+
print(P)
|
|
650
|
+
"""
|
|
651
|
+
species_order, _rule_order, S_minus, S_plus = build_S_minus_plus(crn)
|
|
652
|
+
S = S_plus - S_minus
|
|
653
|
+
P = _structural_sign_pattern(S, S_minus, tol=tol)
|
|
654
|
+
return species_order, P
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def species_influence_graph(
|
|
658
|
+
crn: Any,
|
|
659
|
+
*,
|
|
660
|
+
tol: float = 1e-12,
|
|
661
|
+
use_labels: bool = False,
|
|
662
|
+
) -> nx.DiGraph:
|
|
663
|
+
"""Build the species influence graph induced by the symbolic Jacobian.
|
|
664
|
+
|
|
665
|
+
Nodes represent species. A directed edge ``u -> v`` is added when species
|
|
666
|
+
``u`` can structurally influence species ``v`` in the local linearized
|
|
667
|
+
dynamics.
|
|
668
|
+
|
|
669
|
+
Edge attribute ``sign`` is one of:
|
|
670
|
+
|
|
671
|
+
- ``"+"``
|
|
672
|
+
- ``"-"``
|
|
673
|
+
- ``"mixed"``
|
|
674
|
+
|
|
675
|
+
Additional edge attributes record the original source and target species
|
|
676
|
+
node identifiers.
|
|
677
|
+
|
|
678
|
+
:param crn:
|
|
679
|
+
SynCRN graph or SynCRN-like object in the species/rule representation.
|
|
680
|
+
:type crn:
|
|
681
|
+
Any
|
|
682
|
+
:param tol:
|
|
683
|
+
Tolerance used for structural zero testing.
|
|
684
|
+
:type tol:
|
|
685
|
+
float
|
|
686
|
+
:param use_labels:
|
|
687
|
+
If ``True`` and ``crn`` is a NetworkX graph, use node labels when
|
|
688
|
+
available; otherwise use original node identifiers.
|
|
689
|
+
:type use_labels:
|
|
690
|
+
bool
|
|
691
|
+
|
|
692
|
+
:return:
|
|
693
|
+
Directed species influence graph.
|
|
694
|
+
:rtype:
|
|
695
|
+
nx.DiGraph
|
|
696
|
+
|
|
697
|
+
.. rubric:: Example
|
|
698
|
+
|
|
699
|
+
.. code-block:: python
|
|
700
|
+
|
|
701
|
+
G_inf = species_influence_graph(crn, use_labels=True)
|
|
702
|
+
|
|
703
|
+
print(G_inf.nodes(data=True))
|
|
704
|
+
print(G_inf.edges(data=True))
|
|
705
|
+
"""
|
|
706
|
+
species_order, P = jacobian_sign_pattern(crn, tol=tol)
|
|
707
|
+
|
|
708
|
+
label_map: Dict[Any, Any] = {}
|
|
709
|
+
if use_labels and isinstance(
|
|
710
|
+
crn, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
|
|
711
|
+
):
|
|
712
|
+
for s in species_order:
|
|
713
|
+
label_map[s] = crn.nodes[s].get("label", s)
|
|
714
|
+
else:
|
|
715
|
+
for s in species_order:
|
|
716
|
+
label_map[s] = s
|
|
717
|
+
|
|
718
|
+
G_inf = nx.DiGraph()
|
|
719
|
+
for s in species_order:
|
|
720
|
+
G_inf.add_node(label_map[s], source_node=s)
|
|
721
|
+
|
|
722
|
+
n_species = len(species_order)
|
|
723
|
+
for i in range(n_species): # target row
|
|
724
|
+
for k in range(n_species): # source column
|
|
725
|
+
sign = str(P[i, k])
|
|
726
|
+
if sign != "0":
|
|
727
|
+
G_inf.add_edge(
|
|
728
|
+
label_map[species_order[k]],
|
|
729
|
+
label_map[species_order[i]],
|
|
730
|
+
sign=sign,
|
|
731
|
+
source_species=species_order[k],
|
|
732
|
+
target_species=species_order[i],
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
return G_inf
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def structural_singularity_summary(
|
|
739
|
+
crn: Any,
|
|
740
|
+
*,
|
|
741
|
+
tol: float = 1e-12,
|
|
742
|
+
max_exact_size: int = 7,
|
|
743
|
+
symbol_prefix: str = "rprime",
|
|
744
|
+
) -> StructuralSingularitySummary:
|
|
745
|
+
"""Diagnose structural singularity of the symbolic Jacobian.
|
|
746
|
+
|
|
747
|
+
This routine performs two levels of analysis:
|
|
748
|
+
|
|
749
|
+
1. structural-rank and perfect-matching diagnostics on the Jacobian
|
|
750
|
+
sparsity pattern, and
|
|
751
|
+
2. optional exact symbolic determinant evaluation for sufficiently small
|
|
752
|
+
systems.
|
|
753
|
+
|
|
754
|
+
Pattern-level singularity indicates that the Jacobian is singular for all
|
|
755
|
+
admissible parameter values consistent with the sparsity structure. If the
|
|
756
|
+
pattern is not singular, an exact determinant test may still detect
|
|
757
|
+
symbolic cancellation and prove singularity for small systems.
|
|
758
|
+
|
|
759
|
+
:param crn:
|
|
760
|
+
SynCRN graph or SynCRN-like object in the species/rule representation.
|
|
761
|
+
:type crn:
|
|
762
|
+
Any
|
|
763
|
+
:param tol:
|
|
764
|
+
Tolerance used for structural zero testing.
|
|
765
|
+
:type tol:
|
|
766
|
+
float
|
|
767
|
+
:param max_exact_size:
|
|
768
|
+
Maximum number of species for which the exact symbolic determinant is
|
|
769
|
+
computed.
|
|
770
|
+
:type max_exact_size:
|
|
771
|
+
int
|
|
772
|
+
:param symbol_prefix:
|
|
773
|
+
Prefix used for symbolic reactivity variables.
|
|
774
|
+
:type symbol_prefix:
|
|
775
|
+
str
|
|
776
|
+
|
|
777
|
+
:return:
|
|
778
|
+
Structured summary of Jacobian structural-singularity diagnostics.
|
|
779
|
+
:rtype:
|
|
780
|
+
StructuralSingularitySummary
|
|
781
|
+
|
|
782
|
+
:raises ImportError:
|
|
783
|
+
If exact symbolic determinant evaluation is requested but
|
|
784
|
+
:mod:`sympy` is not available.
|
|
785
|
+
|
|
786
|
+
.. rubric:: Example
|
|
787
|
+
|
|
788
|
+
.. code-block:: python
|
|
789
|
+
|
|
790
|
+
summary = structural_singularity_summary(
|
|
791
|
+
crn,
|
|
792
|
+
max_exact_size=6,
|
|
793
|
+
symbol_prefix="rprime",
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
print(summary)
|
|
797
|
+
print(summary.to_dict())
|
|
798
|
+
print(summary.classification)
|
|
799
|
+
"""
|
|
800
|
+
species_order, A = jacobian_sparsity(crn, tol=tol)
|
|
801
|
+
n_species = len(species_order)
|
|
802
|
+
|
|
803
|
+
B, row_nodes, _col_nodes = _jacobian_pattern_bipartite(A)
|
|
804
|
+
matching = nx_bipartite.maximum_matching(B, top_nodes=set(row_nodes))
|
|
805
|
+
structural_rank = sum(1 for u in row_nodes if u in matching)
|
|
806
|
+
has_perfect_matching = structural_rank == n_species
|
|
807
|
+
pattern_singular = structural_rank < n_species
|
|
808
|
+
|
|
809
|
+
determinant_checked = False
|
|
810
|
+
determinant_expr: Optional[Any] = None
|
|
811
|
+
determinant_is_zero: Optional[bool] = None
|
|
812
|
+
|
|
813
|
+
if n_species <= max_exact_size:
|
|
814
|
+
_require_sympy()
|
|
815
|
+
determinant_checked = True
|
|
816
|
+
_sp_order, _rule_order, G = symbolic_jacobian(
|
|
817
|
+
crn,
|
|
818
|
+
symbol_prefix=symbol_prefix,
|
|
819
|
+
tol=tol,
|
|
820
|
+
)
|
|
821
|
+
determinant_expr = sp.simplify(G.det())
|
|
822
|
+
determinant_is_zero = bool(determinant_expr == 0)
|
|
823
|
+
|
|
824
|
+
return StructuralSingularitySummary(
|
|
825
|
+
n_species=n_species,
|
|
826
|
+
structural_rank=structural_rank,
|
|
827
|
+
has_perfect_matching=has_perfect_matching,
|
|
828
|
+
pattern_singular=pattern_singular,
|
|
829
|
+
determinant_checked=determinant_checked,
|
|
830
|
+
determinant_expr=determinant_expr,
|
|
831
|
+
determinant_is_zero=determinant_is_zero,
|
|
832
|
+
)
|