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,751 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextvars import ContextVar
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from fractions import Fraction
|
|
6
|
+
from functools import wraps
|
|
7
|
+
from math import gcd
|
|
8
|
+
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from scipy.linalg import null_space as scipy_null_space # type: ignore
|
|
14
|
+
|
|
15
|
+
_SCIPY_AVAILABLE = True
|
|
16
|
+
except Exception:
|
|
17
|
+
scipy_null_space = None # type: ignore
|
|
18
|
+
_SCIPY_AVAILABLE = False
|
|
19
|
+
|
|
20
|
+
from .helper import (
|
|
21
|
+
_as_graph,
|
|
22
|
+
_edge_coeff,
|
|
23
|
+
_is_rule_node,
|
|
24
|
+
_is_species_node,
|
|
25
|
+
_normalize_role,
|
|
26
|
+
_species_and_rule_order,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"build_S_minus_plus",
|
|
31
|
+
"build_S",
|
|
32
|
+
"stoichiometric_matrix",
|
|
33
|
+
"stoichiometric_rank",
|
|
34
|
+
"left_nullspace",
|
|
35
|
+
"right_nullspace",
|
|
36
|
+
"left_right_kernels",
|
|
37
|
+
"integer_conservation_laws",
|
|
38
|
+
"conserved_moieties",
|
|
39
|
+
"StoichSummary",
|
|
40
|
+
"summary",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# S⁻, S⁺ and S = S⁺ − S⁻
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _resolve_species_rule_incidence(
|
|
49
|
+
G: Any,
|
|
50
|
+
u: Any,
|
|
51
|
+
v: Any,
|
|
52
|
+
) -> Optional[Tuple[Any, Any]]:
|
|
53
|
+
"""Resolve an edge endpoint pair into a ``(species_node, rule_node)`` pair.
|
|
54
|
+
|
|
55
|
+
This helper inspects node metadata and accepts either orientation
|
|
56
|
+
``species -> rule`` or ``rule -> species``. If the endpoints do not form
|
|
57
|
+
a valid species-rule incidence, ``None`` is returned.
|
|
58
|
+
|
|
59
|
+
:param G: Graph containing the edge endpoints.
|
|
60
|
+
:type G: Any
|
|
61
|
+
:param u: First endpoint.
|
|
62
|
+
:type u: Any
|
|
63
|
+
:param v: Second endpoint.
|
|
64
|
+
:type v: Any
|
|
65
|
+
:return:
|
|
66
|
+
A pair ``(species_node, rule_node)`` when the endpoints define a valid
|
|
67
|
+
species-rule incidence, otherwise ``None``.
|
|
68
|
+
:rtype: Optional[Tuple[Any, Any]]
|
|
69
|
+
"""
|
|
70
|
+
u_data = G.nodes[u]
|
|
71
|
+
v_data = G.nodes[v]
|
|
72
|
+
|
|
73
|
+
if _is_species_node(u_data) and _is_rule_node(v_data):
|
|
74
|
+
return u, v
|
|
75
|
+
if _is_rule_node(u_data) and _is_species_node(v_data):
|
|
76
|
+
return v, u
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _accumulate_stoich_entry(
|
|
81
|
+
S_minus: np.ndarray,
|
|
82
|
+
S_plus: np.ndarray,
|
|
83
|
+
i: int,
|
|
84
|
+
j: int,
|
|
85
|
+
role: Optional[str],
|
|
86
|
+
coeff: float,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Accumulate one stoichiometric coefficient into ``S^-`` or ``S^+``.
|
|
89
|
+
|
|
90
|
+
:param S_minus: Reactant stoichiometric matrix.
|
|
91
|
+
:type S_minus: np.ndarray
|
|
92
|
+
:param S_plus: Product stoichiometric matrix.
|
|
93
|
+
:type S_plus: np.ndarray
|
|
94
|
+
:param i: Species row index.
|
|
95
|
+
:type i: int
|
|
96
|
+
:param j: Rule column index.
|
|
97
|
+
:type j: int
|
|
98
|
+
:param role:
|
|
99
|
+
Normalized edge role. Expected values are ``"reactant"``,
|
|
100
|
+
``"product"``, or ``None``.
|
|
101
|
+
:type role: Optional[str]
|
|
102
|
+
:param coeff: Stoichiometric coefficient to add.
|
|
103
|
+
:type coeff: float
|
|
104
|
+
:return: ``None``. The matrices are modified in place.
|
|
105
|
+
:rtype: None
|
|
106
|
+
"""
|
|
107
|
+
if role == "reactant":
|
|
108
|
+
S_minus[i, j] += coeff
|
|
109
|
+
elif role == "product":
|
|
110
|
+
S_plus[i, j] += coeff
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _iter_graph_edges_with_data(G: Any) -> Iterable[Tuple[Any, Any, Dict[str, Any]]]:
|
|
114
|
+
"""Yield graph edges as ``(u, v, data)`` triples for simple and multigraphs.
|
|
115
|
+
|
|
116
|
+
For multigraphs, parallel edges are yielded individually while discarding
|
|
117
|
+
the internal edge key.
|
|
118
|
+
|
|
119
|
+
:param G: NetworkX graph-like object.
|
|
120
|
+
:type G: Any
|
|
121
|
+
:return:
|
|
122
|
+
Iterable of edge triples ``(u, v, data)`` where ``data`` is the edge
|
|
123
|
+
attribute mapping.
|
|
124
|
+
:rtype: Iterable[Tuple[Any, Any, Dict[str, Any]]]
|
|
125
|
+
"""
|
|
126
|
+
if G.is_multigraph():
|
|
127
|
+
for u, v, _k, data in G.edges(data=True, keys=True):
|
|
128
|
+
yield u, v, data
|
|
129
|
+
else:
|
|
130
|
+
for u, v, data in G.edges(data=True):
|
|
131
|
+
yield u, v, data
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def build_S_minus_plus(
|
|
135
|
+
crn: Any,
|
|
136
|
+
) -> Tuple[List[Any], List[Any], np.ndarray, np.ndarray]:
|
|
137
|
+
"""Build the reactant matrix ``S^-`` and product matrix ``S^+`` from a SynCRN.
|
|
138
|
+
|
|
139
|
+
The input is interpreted as a bipartite species-rule incidence graph. Rows
|
|
140
|
+
correspond to species nodes and columns correspond to rule nodes.
|
|
141
|
+
|
|
142
|
+
Graph conventions
|
|
143
|
+
-----------------
|
|
144
|
+
Nodes:
|
|
145
|
+
- species nodes have ``kind="species"``
|
|
146
|
+
- rule nodes have ``kind="rule"``
|
|
147
|
+
|
|
148
|
+
Edges:
|
|
149
|
+
- ``role``: ``"reactant"`` or ``"product"``
|
|
150
|
+
- ``stoich``: stoichiometric coefficient, default ``1.0``
|
|
151
|
+
|
|
152
|
+
Orientation
|
|
153
|
+
-----------
|
|
154
|
+
The graph may be directed or undirected. The edge ``role`` attribute is
|
|
155
|
+
treated as the source of truth, and endpoint order is not assumed to encode
|
|
156
|
+
reactant/product direction.
|
|
157
|
+
|
|
158
|
+
:param crn:
|
|
159
|
+
A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
160
|
+
:type crn: Any
|
|
161
|
+
:return:
|
|
162
|
+
A 4-tuple ``(species_order, rule_order, S_minus, S_plus)`` where
|
|
163
|
+
``species_order`` defines row order, ``rule_order`` defines column
|
|
164
|
+
order, ``S_minus`` contains reactant stoichiometries, and ``S_plus``
|
|
165
|
+
contains product stoichiometries.
|
|
166
|
+
:rtype: Tuple[List[Any], List[Any], np.ndarray, np.ndarray]
|
|
167
|
+
"""
|
|
168
|
+
cache = _MATRIX_CONTEXT.get()
|
|
169
|
+
if cache is not None and id(crn) in cache:
|
|
170
|
+
return cache[id(crn)][1]
|
|
171
|
+
G = _as_graph(crn)
|
|
172
|
+
|
|
173
|
+
species_order, rule_order, species_index, rule_index = _species_and_rule_order(G)
|
|
174
|
+
|
|
175
|
+
n_species = len(species_order)
|
|
176
|
+
n_rules = len(rule_order)
|
|
177
|
+
|
|
178
|
+
S_minus = np.zeros((n_species, n_rules), dtype=float)
|
|
179
|
+
S_plus = np.zeros((n_species, n_rules), dtype=float)
|
|
180
|
+
|
|
181
|
+
infer_roles = G.is_directed() and not any(
|
|
182
|
+
data.get("role") is not None for _, _, data in _iter_graph_edges_with_data(G)
|
|
183
|
+
)
|
|
184
|
+
for u, v, data in _iter_graph_edges_with_data(G):
|
|
185
|
+
role = _normalize_role(data.get("role"))
|
|
186
|
+
if role is None and data.get("role") is not None:
|
|
187
|
+
continue
|
|
188
|
+
if role is None and infer_roles:
|
|
189
|
+
role = "reactant" if _is_species_node(G.nodes[u]) else "product"
|
|
190
|
+
if role is None:
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
incidence = _resolve_species_rule_incidence(G, u, v)
|
|
194
|
+
if incidence is None:
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
s_node, r_node = incidence
|
|
198
|
+
i = species_index[s_node]
|
|
199
|
+
j = rule_index[r_node]
|
|
200
|
+
coeff = _edge_coeff(data)
|
|
201
|
+
|
|
202
|
+
_accumulate_stoich_entry(S_minus, S_plus, i, j, role, coeff)
|
|
203
|
+
|
|
204
|
+
result = (species_order, rule_order, S_minus, S_plus)
|
|
205
|
+
if cache is not None:
|
|
206
|
+
cache[id(crn)] = (crn, result)
|
|
207
|
+
return result
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def build_S(crn: Any) -> Tuple[List[Any], List[Any], np.ndarray]:
|
|
211
|
+
"""Build the stoichiometric matrix ``S = S^+ - S^-``.
|
|
212
|
+
|
|
213
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
214
|
+
:type crn: Any
|
|
215
|
+
:return:
|
|
216
|
+
A 3-tuple ``(species_order, rule_order, S)`` where ``S`` is the species
|
|
217
|
+
x rule stoichiometric matrix.
|
|
218
|
+
:rtype: Tuple[List[Any], List[Any], np.ndarray]
|
|
219
|
+
"""
|
|
220
|
+
species_order, rule_order, S_minus, S_plus = build_S_minus_plus(crn)
|
|
221
|
+
S = S_plus - S_minus
|
|
222
|
+
return species_order, rule_order, S
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def stoichiometric_matrix(crn: Any) -> np.ndarray:
|
|
226
|
+
"""Return the species x rule stoichiometric matrix ``S``.
|
|
227
|
+
|
|
228
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
229
|
+
:type crn: Any
|
|
230
|
+
:return: Stoichiometric matrix with species as rows and rule nodes as columns.
|
|
231
|
+
:rtype: np.ndarray
|
|
232
|
+
"""
|
|
233
|
+
_, _, S = build_S(crn)
|
|
234
|
+
return S
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def stoichiometric_rank(
|
|
238
|
+
crn: Any, *, tol: float = 1e-10, backend="auto", library_path=None
|
|
239
|
+
) -> int:
|
|
240
|
+
"""Compute the numerical rank of the stoichiometric matrix.
|
|
241
|
+
|
|
242
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
243
|
+
:type crn: Any
|
|
244
|
+
:param tol: Numerical tolerance passed to ``numpy.linalg.matrix_rank``.
|
|
245
|
+
:type tol: float
|
|
246
|
+
:return: Rank of the stoichiometric matrix.
|
|
247
|
+
:rtype: int
|
|
248
|
+
"""
|
|
249
|
+
from ..backend import resolve_backend
|
|
250
|
+
|
|
251
|
+
backend, library_path = resolve_backend(backend, library_path)
|
|
252
|
+
if backend == "cpp":
|
|
253
|
+
from ..native_analysis import matrix_analysis
|
|
254
|
+
|
|
255
|
+
return matrix_analysis(
|
|
256
|
+
stoichiometric_matrix(crn), library_path=library_path, kernels="none"
|
|
257
|
+
)["rank"]
|
|
258
|
+
S = stoichiometric_matrix(crn)
|
|
259
|
+
return int(np.linalg.matrix_rank(S, tol=tol))
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# ---------------------------------------------------------------------------
|
|
263
|
+
# Nullspaces: left and right kernels
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _svd_null_space(A: np.ndarray, rtol: float = 1e-12) -> np.ndarray:
|
|
268
|
+
"""Compute a null-space basis using SVD.
|
|
269
|
+
|
|
270
|
+
This is used as a fallback when SciPy is unavailable.
|
|
271
|
+
|
|
272
|
+
:param A: Input matrix.
|
|
273
|
+
:type A: np.ndarray
|
|
274
|
+
:param rtol: Relative singular-value threshold for rank detection.
|
|
275
|
+
:type rtol: float
|
|
276
|
+
:return:
|
|
277
|
+
Matrix whose columns form a basis of the null space of ``A``.
|
|
278
|
+
:rtype: np.ndarray
|
|
279
|
+
"""
|
|
280
|
+
_u, s, vh = np.linalg.svd(A, full_matrices=True)
|
|
281
|
+
if s.size == 0:
|
|
282
|
+
return np.eye(A.shape[1])
|
|
283
|
+
|
|
284
|
+
tol = rtol * s[0]
|
|
285
|
+
rank = int((s > tol).sum())
|
|
286
|
+
ns = vh[rank:].T
|
|
287
|
+
|
|
288
|
+
if ns.size == 0:
|
|
289
|
+
return np.zeros((A.shape[1], 0))
|
|
290
|
+
|
|
291
|
+
return ns
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _null_space(A: np.ndarray, rtol: float = 1e-12) -> np.ndarray:
|
|
295
|
+
"""Compute a null-space basis using SciPy when available, otherwise SVD.
|
|
296
|
+
|
|
297
|
+
:param A: Input matrix.
|
|
298
|
+
:type A: np.ndarray
|
|
299
|
+
:param rtol: Relative threshold used to determine the numerical null space.
|
|
300
|
+
:type rtol: float
|
|
301
|
+
:return:
|
|
302
|
+
Matrix whose columns form a basis of the null space of ``A``.
|
|
303
|
+
:rtype: np.ndarray
|
|
304
|
+
"""
|
|
305
|
+
if _SCIPY_AVAILABLE and scipy_null_space is not None:
|
|
306
|
+
return scipy_null_space(A, rcond=rtol)
|
|
307
|
+
return _svd_null_space(A, rtol=rtol)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def left_nullspace(crn: Any, *, rtol: float = 1e-12) -> np.ndarray:
|
|
311
|
+
"""Compute a basis for the left null space ``ker(S^T)``.
|
|
312
|
+
|
|
313
|
+
In CRN language, these directions correspond to conservation-law vectors
|
|
314
|
+
over species.
|
|
315
|
+
|
|
316
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
317
|
+
:type crn: Any
|
|
318
|
+
:param rtol: Relative tolerance used in null-space computation.
|
|
319
|
+
:type rtol: float
|
|
320
|
+
:return:
|
|
321
|
+
Matrix whose columns form a basis of ``ker(S^T)``.
|
|
322
|
+
:rtype: np.ndarray
|
|
323
|
+
"""
|
|
324
|
+
S = stoichiometric_matrix(crn)
|
|
325
|
+
return _null_space(S.T, rtol=rtol)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def right_nullspace(crn: Any, *, rtol: float = 1e-12) -> np.ndarray:
|
|
329
|
+
"""Compute a basis for the right null space ``ker(S)``.
|
|
330
|
+
|
|
331
|
+
In CRN or Petri-net language, these directions correspond to rule-flux
|
|
332
|
+
modes or T-semiflows.
|
|
333
|
+
|
|
334
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
335
|
+
:type crn: Any
|
|
336
|
+
:param rtol: Relative tolerance used in null-space computation.
|
|
337
|
+
:type rtol: float
|
|
338
|
+
:return:
|
|
339
|
+
Matrix whose columns form a basis of ``ker(S)``.
|
|
340
|
+
:rtype: np.ndarray
|
|
341
|
+
"""
|
|
342
|
+
S = stoichiometric_matrix(crn)
|
|
343
|
+
return _null_space(S, rtol=rtol)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def left_right_kernels(
|
|
347
|
+
crn: Any,
|
|
348
|
+
*,
|
|
349
|
+
rtol: float = 1e-12,
|
|
350
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
351
|
+
"""Compute both left and right kernels of the stoichiometric matrix.
|
|
352
|
+
|
|
353
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
354
|
+
:type crn: Any
|
|
355
|
+
:param rtol: Relative tolerance used in null-space computation.
|
|
356
|
+
:type rtol: float
|
|
357
|
+
:return:
|
|
358
|
+
Pair ``(left_basis, right_basis)`` where ``left_basis`` spans
|
|
359
|
+
``ker(S^T)`` and ``right_basis`` spans ``ker(S)``.
|
|
360
|
+
:rtype: Tuple[np.ndarray, np.ndarray]
|
|
361
|
+
"""
|
|
362
|
+
left_basis = left_nullspace(crn, rtol=rtol)
|
|
363
|
+
right_basis = right_nullspace(crn, rtol=rtol)
|
|
364
|
+
return left_basis, right_basis
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# ---------------------------------------------------------------------------
|
|
368
|
+
# Integer scaling helpers
|
|
369
|
+
# ---------------------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _lcm(a: int, b: int) -> int:
|
|
373
|
+
"""Compute the least common multiple of two integers.
|
|
374
|
+
|
|
375
|
+
:param a: First integer.
|
|
376
|
+
:type a: int
|
|
377
|
+
:param b: Second integer.
|
|
378
|
+
:type b: int
|
|
379
|
+
:return: Least common multiple of ``a`` and ``b``.
|
|
380
|
+
:rtype: int
|
|
381
|
+
"""
|
|
382
|
+
return abs(a // gcd(a, b) * b) if a and b else abs(a or b)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _vector_to_minimal_integer(vec: np.ndarray, *, tol: float = 1e-12) -> List[int]:
|
|
386
|
+
"""Legacy approximate conversion; production invariants use exact elimination.
|
|
387
|
+
|
|
388
|
+
Retained for callers of the historical private helper. There is no rounded
|
|
389
|
+
integer fallback: coefficients are reconstructed as bounded rationals.
|
|
390
|
+
"""
|
|
391
|
+
values = np.asarray(vec, dtype=float).reshape(-1)
|
|
392
|
+
if not np.all(np.isfinite(values)):
|
|
393
|
+
raise ValueError("Vector entries must be finite")
|
|
394
|
+
scale = np.max(np.abs(values), initial=0)
|
|
395
|
+
if scale <= tol:
|
|
396
|
+
return [0] * len(values)
|
|
397
|
+
fractions = [
|
|
398
|
+
(
|
|
399
|
+
Fraction(0)
|
|
400
|
+
if abs(v / scale) <= tol
|
|
401
|
+
else Fraction(float(v / scale)).limit_denominator(10**6)
|
|
402
|
+
)
|
|
403
|
+
for v in values
|
|
404
|
+
]
|
|
405
|
+
result = _primitive_integer_vector(fractions)
|
|
406
|
+
sign = next((v for v in fractions if v), Fraction(0))
|
|
407
|
+
return [-v for v in result] if sign < 0 else result
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _primitive_integer_vector(vec: Sequence[Fraction]) -> List[int]:
|
|
411
|
+
"""Scale an exact rational vector to a primitive integer vector.
|
|
412
|
+
|
|
413
|
+
The vector is multiplied by the least common multiple of its denominators,
|
|
414
|
+
divided by the greatest common divisor of the result, and sign-normalized so
|
|
415
|
+
that the first nonzero entry is positive.
|
|
416
|
+
|
|
417
|
+
:param vec: Exact rational vector.
|
|
418
|
+
:type vec: Sequence[Fraction]
|
|
419
|
+
:return: Primitive integer vector.
|
|
420
|
+
:rtype: List[int]
|
|
421
|
+
"""
|
|
422
|
+
den_lcm = 1
|
|
423
|
+
for value in vec:
|
|
424
|
+
den_lcm = _lcm(den_lcm, value.denominator)
|
|
425
|
+
|
|
426
|
+
ints = [int(value * den_lcm) for value in vec]
|
|
427
|
+
|
|
428
|
+
divisor = 0
|
|
429
|
+
for value in ints:
|
|
430
|
+
divisor = gcd(divisor, abs(value))
|
|
431
|
+
if divisor > 1:
|
|
432
|
+
ints = [value // divisor for value in ints]
|
|
433
|
+
|
|
434
|
+
for value in ints:
|
|
435
|
+
if value != 0:
|
|
436
|
+
if value < 0:
|
|
437
|
+
ints = [-x for x in ints]
|
|
438
|
+
break
|
|
439
|
+
|
|
440
|
+
return ints
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _exact_null_space(rows: Sequence[Sequence[Any]], n_cols: int) -> List[List[int]]:
|
|
444
|
+
"""Compute an exact integer basis of the null space of a rational matrix.
|
|
445
|
+
|
|
446
|
+
Row-reduces over :class:`~fractions.Fraction`, so the result is exact: every
|
|
447
|
+
returned vector ``v`` satisfies ``M v = 0`` identically rather than to
|
|
448
|
+
within a tolerance.
|
|
449
|
+
|
|
450
|
+
:param rows: Matrix rows.
|
|
451
|
+
:type rows: Sequence[Sequence[Any]]
|
|
452
|
+
:param n_cols: Number of columns.
|
|
453
|
+
:type n_cols: int
|
|
454
|
+
:return: Primitive integer basis vectors of the null space.
|
|
455
|
+
:rtype: List[List[int]]
|
|
456
|
+
"""
|
|
457
|
+
if n_cols == 0:
|
|
458
|
+
return []
|
|
459
|
+
|
|
460
|
+
mat: List[List[Fraction]] = [
|
|
461
|
+
[
|
|
462
|
+
Fraction(str(value)) if not isinstance(value, int) else Fraction(value)
|
|
463
|
+
for value in row
|
|
464
|
+
]
|
|
465
|
+
for row in rows
|
|
466
|
+
]
|
|
467
|
+
|
|
468
|
+
pivot_cols: List[int] = []
|
|
469
|
+
pivot_row = 0
|
|
470
|
+
for col in range(n_cols):
|
|
471
|
+
if pivot_row >= len(mat):
|
|
472
|
+
break
|
|
473
|
+
candidate = next(
|
|
474
|
+
(r for r in range(pivot_row, len(mat)) if mat[r][col] != 0), None
|
|
475
|
+
)
|
|
476
|
+
if candidate is None:
|
|
477
|
+
continue
|
|
478
|
+
|
|
479
|
+
mat[pivot_row], mat[candidate] = mat[candidate], mat[pivot_row]
|
|
480
|
+
pivot_value = mat[pivot_row][col]
|
|
481
|
+
mat[pivot_row] = [value / pivot_value for value in mat[pivot_row]]
|
|
482
|
+
|
|
483
|
+
for r in range(len(mat)):
|
|
484
|
+
if r != pivot_row and mat[r][col] != 0:
|
|
485
|
+
factor = mat[r][col]
|
|
486
|
+
mat[r] = [a - factor * b for a, b in zip(mat[r], mat[pivot_row])]
|
|
487
|
+
|
|
488
|
+
pivot_cols.append(col)
|
|
489
|
+
pivot_row += 1
|
|
490
|
+
|
|
491
|
+
free_cols = [c for c in range(n_cols) if c not in pivot_cols]
|
|
492
|
+
|
|
493
|
+
basis: List[List[int]] = []
|
|
494
|
+
for free_col in free_cols:
|
|
495
|
+
vector = [Fraction(0)] * n_cols
|
|
496
|
+
vector[free_col] = Fraction(1)
|
|
497
|
+
for row_index, pivot_col in enumerate(pivot_cols):
|
|
498
|
+
vector[pivot_col] = -mat[row_index][free_col]
|
|
499
|
+
basis.append(_primitive_integer_vector(vector))
|
|
500
|
+
|
|
501
|
+
return basis
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def integer_conservation_laws(
|
|
505
|
+
crn: Any, *, rtol: float = 1e-12, backend="auto", library_path=None, time_limit=30.0
|
|
506
|
+
) -> List[List[int]]:
|
|
507
|
+
"""Return an exact integer basis for ``ker(S^T)``, the conservation laws.
|
|
508
|
+
|
|
509
|
+
A conservation law is a vector ``y`` over species with ``y^T S = 0``: the
|
|
510
|
+
quantity ``y . c`` is constant along every trajectory, whatever the kinetics.
|
|
511
|
+
Basis vectors are exact primitive integers obtained by rational row
|
|
512
|
+
reduction, so ``y^T S`` is identically zero rather than zero to within a
|
|
513
|
+
tolerance.
|
|
514
|
+
|
|
515
|
+
Basis vectors may contain negative entries. For the non-negative,
|
|
516
|
+
inclusion-minimal laws — the *conserved moieties*, which are the ones with a
|
|
517
|
+
chemical reading such as "total enzyme" or "total phosphate" — use
|
|
518
|
+
:func:`conserved_moieties`.
|
|
519
|
+
|
|
520
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
521
|
+
:type crn: Any
|
|
522
|
+
:param rtol:
|
|
523
|
+
Retained for backward compatibility and ignored; the computation is
|
|
524
|
+
exact and needs no tolerance.
|
|
525
|
+
:type rtol: float
|
|
526
|
+
:return:
|
|
527
|
+
Exact integer basis of the left kernel, one law per row.
|
|
528
|
+
:rtype: List[List[int]]
|
|
529
|
+
|
|
530
|
+
.. rubric:: Example
|
|
531
|
+
|
|
532
|
+
.. code-block:: python
|
|
533
|
+
|
|
534
|
+
crn = SynCRN.from_reaction_strings(["A>>B", "B>>A"])
|
|
535
|
+
print(integer_conservation_laws(crn))
|
|
536
|
+
# [[1, 1]]
|
|
537
|
+
"""
|
|
538
|
+
from ..backend import resolve_backend
|
|
539
|
+
|
|
540
|
+
backend, library_path = resolve_backend(backend, library_path)
|
|
541
|
+
if backend == "cpp":
|
|
542
|
+
from ..native_analysis import matrix_analysis
|
|
543
|
+
|
|
544
|
+
return matrix_analysis(
|
|
545
|
+
stoichiometric_matrix(crn),
|
|
546
|
+
library_path=library_path,
|
|
547
|
+
kernels="left",
|
|
548
|
+
time_limit=time_limit,
|
|
549
|
+
)["left"]
|
|
550
|
+
S = stoichiometric_matrix(crn)
|
|
551
|
+
if S.size == 0:
|
|
552
|
+
return []
|
|
553
|
+
|
|
554
|
+
n_species, _ = S.shape
|
|
555
|
+
rows = [
|
|
556
|
+
[int(v) if float(v).is_integer() else float(v) for v in S[:, j]]
|
|
557
|
+
for j in range(S.shape[1])
|
|
558
|
+
]
|
|
559
|
+
return _exact_null_space(rows, n_species)
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def conserved_moieties(
|
|
563
|
+
crn: Any, *, backend="auto", library_path=None, **native_options
|
|
564
|
+
) -> List[List[int]]:
|
|
565
|
+
"""Return the minimal non-negative integer conservation laws.
|
|
566
|
+
|
|
567
|
+
These are the P-semiflows of the network's Petri-net view: vectors ``y >= 0``
|
|
568
|
+
with ``y^T S = 0`` and inclusion-minimal support. Each one reads as a
|
|
569
|
+
conserved pool — total enzyme, total phosphate, a carbon backbone — which is
|
|
570
|
+
what makes them the useful form in a metabolic or signalling context.
|
|
571
|
+
|
|
572
|
+
Unlike :func:`integer_conservation_laws`, this is not a basis: a network can
|
|
573
|
+
have more minimal moieties than the dimension of its left kernel, and a
|
|
574
|
+
network with only sign-mixed conservation laws has none at all.
|
|
575
|
+
|
|
576
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
577
|
+
:type crn: Any
|
|
578
|
+
:return:
|
|
579
|
+
Minimal non-negative integer conservation laws, one per row, in the
|
|
580
|
+
canonical species order.
|
|
581
|
+
:rtype: List[List[int]]
|
|
582
|
+
|
|
583
|
+
.. rubric:: Example
|
|
584
|
+
|
|
585
|
+
.. code-block:: python
|
|
586
|
+
|
|
587
|
+
crn = SynCRN.from_reaction_strings(["E+S>>ES", "ES>>E+S", "ES>>E+P"])
|
|
588
|
+
print(conserved_moieties(crn))
|
|
589
|
+
"""
|
|
590
|
+
from ..Petrinet.minimal_semiflows import minimal_semiflows
|
|
591
|
+
|
|
592
|
+
S = stoichiometric_matrix(crn)
|
|
593
|
+
if S.size == 0:
|
|
594
|
+
return []
|
|
595
|
+
return minimal_semiflows(
|
|
596
|
+
S, kind="p", backend=backend, library_path=library_path, **native_options
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
# ---------------------------------------------------------------------------
|
|
601
|
+
# Lightweight structural summary
|
|
602
|
+
# ---------------------------------------------------------------------------
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
@dataclass
|
|
606
|
+
class StoichSummary:
|
|
607
|
+
"""
|
|
608
|
+
Lightweight stoichiometric summary of a SynCRN.
|
|
609
|
+
|
|
610
|
+
The field name ``n_reactions`` is retained for backward compatibility even
|
|
611
|
+
though the current SynCRN representation uses rule nodes as process
|
|
612
|
+
columns.
|
|
613
|
+
|
|
614
|
+
:param n_species: Number of species rows in the stoichiometric matrix.
|
|
615
|
+
:type n_species: int
|
|
616
|
+
:param n_reactions:
|
|
617
|
+
Number of process columns in the stoichiometric matrix. In the current
|
|
618
|
+
representation this corresponds to the number of rule nodes.
|
|
619
|
+
:type n_reactions: int
|
|
620
|
+
:param rank: Numerical rank of the stoichiometric matrix.
|
|
621
|
+
:type rank: int
|
|
622
|
+
"""
|
|
623
|
+
|
|
624
|
+
n_species: int
|
|
625
|
+
n_reactions: int
|
|
626
|
+
rank: int
|
|
627
|
+
|
|
628
|
+
dim_left_kernel: int = field(init=False)
|
|
629
|
+
dim_right_kernel: int = field(init=False)
|
|
630
|
+
|
|
631
|
+
def __post_init__(self) -> None:
|
|
632
|
+
"""
|
|
633
|
+
Validate dimensions and derive kernel dimensions.
|
|
634
|
+
|
|
635
|
+
:raises ValueError:
|
|
636
|
+
If dimensions are negative or if ``rank`` exceeds
|
|
637
|
+
``min(n_species, n_reactions)``.
|
|
638
|
+
"""
|
|
639
|
+
if self.n_species < 0 or self.n_reactions < 0:
|
|
640
|
+
raise ValueError("n_species and n_reactions must be non-negative.")
|
|
641
|
+
if self.rank < 0:
|
|
642
|
+
raise ValueError("rank must be non-negative.")
|
|
643
|
+
if self.rank > min(self.n_species, self.n_reactions):
|
|
644
|
+
raise ValueError(
|
|
645
|
+
f"rank={self.rank} cannot exceed min(n_species, n_reactions) = "
|
|
646
|
+
f"{min(self.n_species, self.n_reactions)}"
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
self.dim_left_kernel = max(self.n_species - self.rank, 0)
|
|
650
|
+
self.dim_right_kernel = max(self.n_reactions - self.rank, 0)
|
|
651
|
+
|
|
652
|
+
@property
|
|
653
|
+
def is_full_rank(self) -> bool:
|
|
654
|
+
"""Whether the stoichiometric matrix has full rank.
|
|
655
|
+
|
|
656
|
+
:return:
|
|
657
|
+
``True`` when ``rank == min(n_species, n_reactions)``,
|
|
658
|
+
otherwise ``False``.
|
|
659
|
+
:rtype: bool
|
|
660
|
+
"""
|
|
661
|
+
return self.rank == min(self.n_species, self.n_reactions)
|
|
662
|
+
|
|
663
|
+
@property
|
|
664
|
+
def is_underdetermined(self) -> bool:
|
|
665
|
+
"""Whether the right kernel is non-trivial.
|
|
666
|
+
|
|
667
|
+
Equivalently, this checks whether ``rank < n_reactions``.
|
|
668
|
+
|
|
669
|
+
:return: ``True`` if ``dim_right_kernel > 0``, else ``False``.
|
|
670
|
+
:rtype: bool
|
|
671
|
+
"""
|
|
672
|
+
return self.rank < self.n_reactions
|
|
673
|
+
|
|
674
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
675
|
+
"""Convert the summary into a plain dictionary.
|
|
676
|
+
|
|
677
|
+
:return: Dictionary representation of the summary.
|
|
678
|
+
:rtype: Dict[str, Any]
|
|
679
|
+
"""
|
|
680
|
+
return {
|
|
681
|
+
"n_species": self.n_species,
|
|
682
|
+
"n_reactions": self.n_reactions,
|
|
683
|
+
"rank": self.rank,
|
|
684
|
+
"dim_left_kernel": self.dim_left_kernel,
|
|
685
|
+
"dim_right_kernel": self.dim_right_kernel,
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
@classmethod
|
|
689
|
+
def from_crn(cls, crn: Any) -> "StoichSummary":
|
|
690
|
+
"""Construct a summary directly from a CRN object or graph.
|
|
691
|
+
|
|
692
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
693
|
+
:type crn: Any
|
|
694
|
+
:return: Stoichiometric summary derived from the CRN.
|
|
695
|
+
:rtype: StoichSummary
|
|
696
|
+
"""
|
|
697
|
+
S = stoichiometric_matrix(crn)
|
|
698
|
+
n_species, n_reactions = S.shape
|
|
699
|
+
# ``matrix_rank`` raises on a zero-size array, which a network with no
|
|
700
|
+
# species or no reactions legitimately produces.
|
|
701
|
+
rank = 0 if S.size == 0 else int(np.linalg.matrix_rank(S))
|
|
702
|
+
return cls(
|
|
703
|
+
n_species=n_species,
|
|
704
|
+
n_reactions=n_reactions,
|
|
705
|
+
rank=rank,
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
def __str__(self) -> str:
|
|
709
|
+
"""Return a human-readable multi-line representation.
|
|
710
|
+
|
|
711
|
+
:return: Formatted summary string.
|
|
712
|
+
:rtype: str
|
|
713
|
+
"""
|
|
714
|
+
lines = [
|
|
715
|
+
"StoichSummary(",
|
|
716
|
+
f" n_species = {self.n_species}",
|
|
717
|
+
f" n_reactions = {self.n_reactions}",
|
|
718
|
+
f" rank = {self.rank}",
|
|
719
|
+
f" dim_left_kernel = {self.dim_left_kernel}",
|
|
720
|
+
f" dim_right_kernel = {self.dim_right_kernel}",
|
|
721
|
+
")",
|
|
722
|
+
]
|
|
723
|
+
return "\n".join(lines)
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def summary(crn: Any) -> StoichSummary:
|
|
727
|
+
"""Compute a quick stoichiometric summary.
|
|
728
|
+
|
|
729
|
+
:param crn: A NetworkX bipartite graph or a SynCRN-like object containing one.
|
|
730
|
+
:type crn: Any
|
|
731
|
+
:return: Lightweight stoichiometric summary of the CRN.
|
|
732
|
+
:rtype: StoichSummary
|
|
733
|
+
"""
|
|
734
|
+
return StoichSummary.from_crn(crn)
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
# This cache exists only during one summary calculation; mutable models are never
|
|
738
|
+
# cached across calls or shared between threads/tasks.
|
|
739
|
+
_MATRIX_CONTEXT = ContextVar("crn_matrix_context", default=None)
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def _reuse_matrices(function):
|
|
743
|
+
@wraps(function)
|
|
744
|
+
def wrapped(*args, **kwargs):
|
|
745
|
+
token = _MATRIX_CONTEXT.set({})
|
|
746
|
+
try:
|
|
747
|
+
return function(*args, **kwargs)
|
|
748
|
+
finally:
|
|
749
|
+
_MATRIX_CONTEXT.reset(token)
|
|
750
|
+
|
|
751
|
+
return wrapped
|