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,708 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from math import cos, pi, sin
|
|
5
|
+
from typing import Callable, Dict, Hashable, Iterable, List, Mapping, Tuple
|
|
6
|
+
|
|
7
|
+
import networkx as nx
|
|
8
|
+
|
|
9
|
+
from ..kinds import is_reaction_node
|
|
10
|
+
|
|
11
|
+
Pos = Dict[Hashable, Tuple[float, float]]
|
|
12
|
+
LayoutFunc = Callable[..., Pos]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _sort_nodes(graph: nx.DiGraph, nodes: Iterable[Hashable]) -> List[Hashable]:
|
|
16
|
+
"""Return nodes in a deterministic visualization order.
|
|
17
|
+
|
|
18
|
+
Nodes are sorted by selected node attributes and finally by node identifier.
|
|
19
|
+
|
|
20
|
+
:param graph: Directed graph containing node metadata.
|
|
21
|
+
:type graph: nx.DiGraph
|
|
22
|
+
:param nodes: Nodes to sort.
|
|
23
|
+
:type nodes: Iterable[Hashable]
|
|
24
|
+
:return: Sorted node identifiers.
|
|
25
|
+
:rtype: List[Hashable]
|
|
26
|
+
"""
|
|
27
|
+
return sorted(
|
|
28
|
+
nodes,
|
|
29
|
+
key=lambda n: (
|
|
30
|
+
graph.nodes[n].get("kind", ""),
|
|
31
|
+
graph.nodes[n].get("step", 10**9),
|
|
32
|
+
graph.nodes[n].get("rule_index", 10**9),
|
|
33
|
+
graph.nodes[n].get("app_index", 10**9),
|
|
34
|
+
str(graph.nodes[n].get("label", "")),
|
|
35
|
+
str(n),
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _stack_vertical(
|
|
41
|
+
graph: nx.DiGraph,
|
|
42
|
+
nodes: Iterable[Hashable],
|
|
43
|
+
*,
|
|
44
|
+
x: float,
|
|
45
|
+
node_spacing: float,
|
|
46
|
+
) -> Pos:
|
|
47
|
+
"""Place nodes in a vertical stack centered around ``y = 0``.
|
|
48
|
+
|
|
49
|
+
:param graph: Directed graph containing node metadata.
|
|
50
|
+
:type graph: nx.DiGraph
|
|
51
|
+
:param nodes: Nodes to place.
|
|
52
|
+
:type nodes: Iterable[Hashable]
|
|
53
|
+
:param x: Shared x-coordinate for the stack.
|
|
54
|
+
:type x: float
|
|
55
|
+
:param node_spacing: Distance between adjacent nodes along the y-axis.
|
|
56
|
+
:type node_spacing: float
|
|
57
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
58
|
+
:rtype: Pos
|
|
59
|
+
"""
|
|
60
|
+
ordered = _sort_nodes(graph, nodes)
|
|
61
|
+
n = len(ordered)
|
|
62
|
+
if n == 0:
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
center = (n - 1) / 2.0
|
|
66
|
+
pos: Pos = {}
|
|
67
|
+
for i, node in enumerate(ordered):
|
|
68
|
+
y = (center - i) * node_spacing
|
|
69
|
+
pos[node] = (x, y)
|
|
70
|
+
return pos
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _stack_horizontal(
|
|
74
|
+
graph: nx.DiGraph,
|
|
75
|
+
nodes: Iterable[Hashable],
|
|
76
|
+
*,
|
|
77
|
+
y: float,
|
|
78
|
+
node_spacing: float,
|
|
79
|
+
) -> Pos:
|
|
80
|
+
"""Place nodes in a horizontal stack centered around ``x = 0``.
|
|
81
|
+
|
|
82
|
+
:param graph: Directed graph containing node metadata.
|
|
83
|
+
:type graph: nx.DiGraph
|
|
84
|
+
:param nodes: Nodes to place.
|
|
85
|
+
:type nodes: Iterable[Hashable]
|
|
86
|
+
:param y: Shared y-coordinate for the stack.
|
|
87
|
+
:type y: float
|
|
88
|
+
:param node_spacing: Distance between adjacent nodes along the x-axis.
|
|
89
|
+
:type node_spacing: float
|
|
90
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
91
|
+
:rtype: Pos
|
|
92
|
+
"""
|
|
93
|
+
ordered = _sort_nodes(graph, nodes)
|
|
94
|
+
n = len(ordered)
|
|
95
|
+
if n == 0:
|
|
96
|
+
return {}
|
|
97
|
+
|
|
98
|
+
center = (n - 1) / 2.0
|
|
99
|
+
pos: Pos = {}
|
|
100
|
+
for i, node in enumerate(ordered):
|
|
101
|
+
x = (i - center) * node_spacing
|
|
102
|
+
pos[node] = (x, y)
|
|
103
|
+
return pos
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _rule_x_from_step(graph: nx.DiGraph, node: Hashable) -> float:
|
|
107
|
+
"""Compute the logical x-layer of a rule node from its step annotation.
|
|
108
|
+
|
|
109
|
+
Rule layers are placed at odd indices: ``1, 3, 5, ...``.
|
|
110
|
+
|
|
111
|
+
:param graph: Directed graph containing node metadata.
|
|
112
|
+
:type graph: nx.DiGraph
|
|
113
|
+
:param node: Rule node identifier.
|
|
114
|
+
:type node: Hashable
|
|
115
|
+
:return: Logical x-layer for the rule node.
|
|
116
|
+
:rtype: float
|
|
117
|
+
"""
|
|
118
|
+
step = graph.nodes[node].get("step")
|
|
119
|
+
return float(2 * int(step) - 1) if step is not None else 1.0
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _species_x_from_incidence(graph: nx.DiGraph, node: Hashable) -> float:
|
|
123
|
+
"""Infer the logical x-layer of a species node from adjacent rule nodes.
|
|
124
|
+
|
|
125
|
+
The heuristic is:
|
|
126
|
+
- if the species is a reactant of one or more rules, place it immediately
|
|
127
|
+
before the earliest such rule
|
|
128
|
+
- otherwise, if it is a product of one or more rules, place it immediately
|
|
129
|
+
after the earliest such rule
|
|
130
|
+
- otherwise place it at layer ``0``
|
|
131
|
+
|
|
132
|
+
:param graph: Directed graph containing node and edge metadata.
|
|
133
|
+
:type graph: nx.DiGraph
|
|
134
|
+
:param node: Species node identifier.
|
|
135
|
+
:type node: Hashable
|
|
136
|
+
:return: Logical x-layer for the species node.
|
|
137
|
+
:rtype: float
|
|
138
|
+
"""
|
|
139
|
+
reactant_steps: List[int] = []
|
|
140
|
+
product_steps: List[int] = []
|
|
141
|
+
|
|
142
|
+
for _, v, d in graph.out_edges(node, data=True):
|
|
143
|
+
if d.get("role") == "reactant" and is_reaction_node(graph.nodes[v]):
|
|
144
|
+
step = graph.nodes[v].get("step", d.get("step"))
|
|
145
|
+
if step is not None:
|
|
146
|
+
reactant_steps.append(int(step))
|
|
147
|
+
|
|
148
|
+
for u, _, d in graph.in_edges(node, data=True):
|
|
149
|
+
if d.get("role") == "product" and is_reaction_node(graph.nodes[u]):
|
|
150
|
+
step = graph.nodes[u].get("step", d.get("step"))
|
|
151
|
+
if step is not None:
|
|
152
|
+
product_steps.append(int(step))
|
|
153
|
+
|
|
154
|
+
if reactant_steps:
|
|
155
|
+
return float(2 * (min(reactant_steps) - 1))
|
|
156
|
+
if product_steps:
|
|
157
|
+
return float(2 * min(product_steps))
|
|
158
|
+
return 0.0
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _logical_layers(
|
|
162
|
+
graph: nx.DiGraph,
|
|
163
|
+
*,
|
|
164
|
+
species_nodes: List[Hashable],
|
|
165
|
+
rule_nodes: List[Hashable],
|
|
166
|
+
) -> Dict[int, List[Hashable]]:
|
|
167
|
+
"""Build integer logical layers for species and rule nodes.
|
|
168
|
+
|
|
169
|
+
:param graph: Directed graph containing node and edge metadata.
|
|
170
|
+
:type graph: nx.DiGraph
|
|
171
|
+
:param species_nodes: List of species node identifiers.
|
|
172
|
+
:type species_nodes: List[Hashable]
|
|
173
|
+
:param rule_nodes: List of rule node identifiers.
|
|
174
|
+
:type rule_nodes: List[Hashable]
|
|
175
|
+
:return: Mapping from integer layer index to nodes in that layer.
|
|
176
|
+
:rtype: Dict[int, List[Hashable]]
|
|
177
|
+
"""
|
|
178
|
+
layers: Dict[int, List[Hashable]] = defaultdict(list)
|
|
179
|
+
|
|
180
|
+
for node in species_nodes:
|
|
181
|
+
layers[int(_species_x_from_incidence(graph, node))].append(node)
|
|
182
|
+
|
|
183
|
+
for node in rule_nodes:
|
|
184
|
+
layers[int(_rule_x_from_step(graph, node))].append(node)
|
|
185
|
+
|
|
186
|
+
return dict(layers)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def step_layout(
|
|
190
|
+
graph: nx.DiGraph,
|
|
191
|
+
*,
|
|
192
|
+
species_nodes: List[Hashable],
|
|
193
|
+
rule_nodes: List[Hashable],
|
|
194
|
+
node_spacing: float = 1.4,
|
|
195
|
+
layer_spacing: float = 2.5,
|
|
196
|
+
**_: object,
|
|
197
|
+
) -> Pos:
|
|
198
|
+
"""Compute a layered step-wise layout.
|
|
199
|
+
|
|
200
|
+
Species and rule nodes are assigned to alternating logical x-layers inferred
|
|
201
|
+
from rule steps and incidence relationships.
|
|
202
|
+
|
|
203
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
204
|
+
:type graph: nx.DiGraph
|
|
205
|
+
:param species_nodes: List of species node identifiers.
|
|
206
|
+
:type species_nodes: List[Hashable]
|
|
207
|
+
:param rule_nodes: List of rule node identifiers.
|
|
208
|
+
:type rule_nodes: List[Hashable]
|
|
209
|
+
:param node_spacing: Vertical spacing between nodes in the same layer.
|
|
210
|
+
:type node_spacing: float
|
|
211
|
+
:param layer_spacing: Horizontal spacing between adjacent layers.
|
|
212
|
+
:type layer_spacing: float
|
|
213
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
214
|
+
:rtype: Pos
|
|
215
|
+
|
|
216
|
+
.. code-block:: python
|
|
217
|
+
|
|
218
|
+
pos = step_layout(
|
|
219
|
+
graph,
|
|
220
|
+
species_nodes=species_nodes,
|
|
221
|
+
rule_nodes=rule_nodes,
|
|
222
|
+
node_spacing=1.6,
|
|
223
|
+
layer_spacing=3.0,
|
|
224
|
+
)
|
|
225
|
+
"""
|
|
226
|
+
groups: Dict[float, List[Hashable]] = defaultdict(list)
|
|
227
|
+
|
|
228
|
+
for node in species_nodes:
|
|
229
|
+
groups[_species_x_from_incidence(graph, node)].append(node)
|
|
230
|
+
|
|
231
|
+
for node in rule_nodes:
|
|
232
|
+
groups[_rule_x_from_step(graph, node)].append(node)
|
|
233
|
+
|
|
234
|
+
pos: Pos = {}
|
|
235
|
+
for raw_x in sorted(groups):
|
|
236
|
+
pos.update(
|
|
237
|
+
_stack_vertical(
|
|
238
|
+
graph,
|
|
239
|
+
groups[raw_x],
|
|
240
|
+
x=raw_x * layer_spacing,
|
|
241
|
+
node_spacing=node_spacing,
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
return pos
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def bipartite_layout(
|
|
248
|
+
graph: nx.DiGraph,
|
|
249
|
+
*,
|
|
250
|
+
species_nodes: List[Hashable],
|
|
251
|
+
rule_nodes: List[Hashable],
|
|
252
|
+
node_spacing: float = 1.4,
|
|
253
|
+
layer_spacing: float = 3.0,
|
|
254
|
+
orientation: str = "vertical",
|
|
255
|
+
**_: object,
|
|
256
|
+
) -> Pos:
|
|
257
|
+
"""Compute a two-layer species-rule layout.
|
|
258
|
+
|
|
259
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
260
|
+
:type graph: nx.DiGraph
|
|
261
|
+
:param species_nodes: List of species node identifiers.
|
|
262
|
+
:type species_nodes: List[Hashable]
|
|
263
|
+
:param rule_nodes: List of rule node identifiers.
|
|
264
|
+
:type rule_nodes: List[Hashable]
|
|
265
|
+
:param node_spacing: Spacing between adjacent nodes within a layer.
|
|
266
|
+
:type node_spacing: float
|
|
267
|
+
:param layer_spacing: Distance between the species and rule layers.
|
|
268
|
+
:type layer_spacing: float
|
|
269
|
+
:param orientation: Layout orientation. Must be ``"vertical"`` or
|
|
270
|
+
``"horizontal"``.
|
|
271
|
+
:type orientation: str
|
|
272
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
273
|
+
:rtype: Pos
|
|
274
|
+
:raises ValueError: If ``orientation`` is not supported.
|
|
275
|
+
"""
|
|
276
|
+
pos: Pos = {}
|
|
277
|
+
|
|
278
|
+
if orientation == "vertical":
|
|
279
|
+
pos.update(
|
|
280
|
+
_stack_vertical(graph, species_nodes, x=0.0, node_spacing=node_spacing)
|
|
281
|
+
)
|
|
282
|
+
pos.update(
|
|
283
|
+
_stack_vertical(
|
|
284
|
+
graph,
|
|
285
|
+
rule_nodes,
|
|
286
|
+
x=layer_spacing,
|
|
287
|
+
node_spacing=node_spacing,
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
return pos
|
|
291
|
+
|
|
292
|
+
if orientation == "horizontal":
|
|
293
|
+
pos.update(
|
|
294
|
+
_stack_horizontal(graph, species_nodes, y=0.0, node_spacing=node_spacing)
|
|
295
|
+
)
|
|
296
|
+
pos.update(
|
|
297
|
+
_stack_horizontal(
|
|
298
|
+
graph,
|
|
299
|
+
rule_nodes,
|
|
300
|
+
y=-layer_spacing,
|
|
301
|
+
node_spacing=node_spacing,
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
return pos
|
|
305
|
+
|
|
306
|
+
raise ValueError("orientation must be 'vertical' or 'horizontal'")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def multipartite_step_layout(
|
|
310
|
+
graph: nx.DiGraph,
|
|
311
|
+
*,
|
|
312
|
+
species_nodes: List[Hashable],
|
|
313
|
+
rule_nodes: List[Hashable],
|
|
314
|
+
**_: object,
|
|
315
|
+
) -> Pos:
|
|
316
|
+
"""Compute a multipartite layout using inferred step layers.
|
|
317
|
+
|
|
318
|
+
This layout is useful for larger step-annotated graphs where a simple custom
|
|
319
|
+
step layout becomes crowded.
|
|
320
|
+
|
|
321
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
322
|
+
:type graph: nx.DiGraph
|
|
323
|
+
:param species_nodes: List of species node identifiers.
|
|
324
|
+
:type species_nodes: List[Hashable]
|
|
325
|
+
:param rule_nodes: List of rule node identifiers.
|
|
326
|
+
:type rule_nodes: List[Hashable]
|
|
327
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
328
|
+
:rtype: Pos
|
|
329
|
+
"""
|
|
330
|
+
G = graph.copy()
|
|
331
|
+
layers = _logical_layers(
|
|
332
|
+
graph,
|
|
333
|
+
species_nodes=species_nodes,
|
|
334
|
+
rule_nodes=rule_nodes,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
node_to_layer = {node: layer for layer, nodes in layers.items() for node in nodes}
|
|
338
|
+
|
|
339
|
+
for node in G.nodes:
|
|
340
|
+
G.nodes[node]["subset"] = node_to_layer.get(node, 0)
|
|
341
|
+
|
|
342
|
+
return nx.multipartite_layout(G, subset_key="subset", align="vertical")
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def radial_step_layout(
|
|
346
|
+
graph: nx.DiGraph,
|
|
347
|
+
*,
|
|
348
|
+
species_nodes: List[Hashable],
|
|
349
|
+
rule_nodes: List[Hashable],
|
|
350
|
+
radius_step: float = 1.8,
|
|
351
|
+
**_: object,
|
|
352
|
+
) -> Pos:
|
|
353
|
+
"""Compute a radial step layout using concentric circles.
|
|
354
|
+
|
|
355
|
+
Logical layers are mapped to increasing radii.
|
|
356
|
+
|
|
357
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
358
|
+
:type graph: nx.DiGraph
|
|
359
|
+
:param species_nodes: List of species node identifiers.
|
|
360
|
+
:type species_nodes: List[Hashable]
|
|
361
|
+
:param rule_nodes: List of rule node identifiers.
|
|
362
|
+
:type rule_nodes: List[Hashable]
|
|
363
|
+
:param radius_step: Radial increment between adjacent logical layers.
|
|
364
|
+
:type radius_step: float
|
|
365
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
366
|
+
:rtype: Pos
|
|
367
|
+
"""
|
|
368
|
+
layers = _logical_layers(
|
|
369
|
+
graph,
|
|
370
|
+
species_nodes=species_nodes,
|
|
371
|
+
rule_nodes=rule_nodes,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
pos: Pos = {}
|
|
375
|
+
for layer in sorted(layers):
|
|
376
|
+
nodes = _sort_nodes(graph, layers[layer])
|
|
377
|
+
n = len(nodes)
|
|
378
|
+
radius = max(1.0, layer * radius_step)
|
|
379
|
+
|
|
380
|
+
for i, node in enumerate(nodes):
|
|
381
|
+
theta = (2.0 * pi * i) / max(n, 1)
|
|
382
|
+
pos[node] = (radius * cos(theta), radius * sin(theta))
|
|
383
|
+
|
|
384
|
+
return pos
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def circular_bipartite_layout(
|
|
388
|
+
graph: nx.DiGraph,
|
|
389
|
+
*,
|
|
390
|
+
species_nodes: List[Hashable],
|
|
391
|
+
rule_nodes: List[Hashable],
|
|
392
|
+
species_radius: float = 2.5,
|
|
393
|
+
rule_radius: float = 4.0,
|
|
394
|
+
**_: object,
|
|
395
|
+
) -> Pos:
|
|
396
|
+
"""Compute a circular bipartite layout using two concentric circles.
|
|
397
|
+
|
|
398
|
+
Species and rule nodes are placed on separate rings.
|
|
399
|
+
|
|
400
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
401
|
+
:type graph: nx.DiGraph
|
|
402
|
+
:param species_nodes: List of species node identifiers.
|
|
403
|
+
:type species_nodes: List[Hashable]
|
|
404
|
+
:param rule_nodes: List of rule node identifiers.
|
|
405
|
+
:type rule_nodes: List[Hashable]
|
|
406
|
+
:param species_radius: Radius of the species ring.
|
|
407
|
+
:type species_radius: float
|
|
408
|
+
:param rule_radius: Radius of the rule ring.
|
|
409
|
+
:type rule_radius: float
|
|
410
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
411
|
+
:rtype: Pos
|
|
412
|
+
"""
|
|
413
|
+
pos: Pos = {}
|
|
414
|
+
s_nodes = _sort_nodes(graph, species_nodes)
|
|
415
|
+
r_nodes = _sort_nodes(graph, rule_nodes)
|
|
416
|
+
|
|
417
|
+
for i, node in enumerate(s_nodes):
|
|
418
|
+
theta = (2.0 * pi * i) / max(len(s_nodes), 1)
|
|
419
|
+
pos[node] = (species_radius * cos(theta), species_radius * sin(theta))
|
|
420
|
+
|
|
421
|
+
for i, node in enumerate(r_nodes):
|
|
422
|
+
theta = (2.0 * pi * i) / max(len(r_nodes), 1)
|
|
423
|
+
pos[node] = (rule_radius * cos(theta), rule_radius * sin(theta))
|
|
424
|
+
|
|
425
|
+
return pos
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def degree_shell_layout(
|
|
429
|
+
graph: nx.DiGraph,
|
|
430
|
+
*,
|
|
431
|
+
species_nodes: List[Hashable],
|
|
432
|
+
rule_nodes: List[Hashable],
|
|
433
|
+
**_: object,
|
|
434
|
+
) -> Pos:
|
|
435
|
+
"""Compute a degree-based shell layout.
|
|
436
|
+
|
|
437
|
+
Nodes with the highest degree are placed in the inner shell, followed by
|
|
438
|
+
medium-degree and lower-degree shells.
|
|
439
|
+
|
|
440
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
441
|
+
:type graph: nx.DiGraph
|
|
442
|
+
:param species_nodes: List of species node identifiers.
|
|
443
|
+
:type species_nodes: List[Hashable]
|
|
444
|
+
:param rule_nodes: List of rule node identifiers.
|
|
445
|
+
:type rule_nodes: List[Hashable]
|
|
446
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
447
|
+
:rtype: Pos
|
|
448
|
+
"""
|
|
449
|
+
by_degree = sorted(graph.degree, key=lambda x: (-x[1], str(x[0])))
|
|
450
|
+
n_total = len(by_degree)
|
|
451
|
+
|
|
452
|
+
q1 = max(1, n_total // 4)
|
|
453
|
+
q2 = max(2, n_total // 2)
|
|
454
|
+
|
|
455
|
+
high = [node for node, _ in by_degree[:q1]]
|
|
456
|
+
mid = [node for node, _ in by_degree[q1:q2]]
|
|
457
|
+
rest = [node for node, _ in by_degree[q2:]]
|
|
458
|
+
|
|
459
|
+
shells = [shell for shell in (high, mid, rest) if shell]
|
|
460
|
+
return nx.shell_layout(graph, nlist=shells)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def choose_auto_layout(
|
|
464
|
+
graph: nx.DiGraph,
|
|
465
|
+
*,
|
|
466
|
+
species_nodes: List[Hashable],
|
|
467
|
+
rule_nodes: List[Hashable],
|
|
468
|
+
) -> str:
|
|
469
|
+
"""Choose a layout heuristically.
|
|
470
|
+
|
|
471
|
+
Preference is given to step-aware layouts when rule step annotations are
|
|
472
|
+
available. Larger or denser graphs are routed to layouts that typically
|
|
473
|
+
reduce clutter.
|
|
474
|
+
|
|
475
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
476
|
+
:type graph: nx.DiGraph
|
|
477
|
+
:param species_nodes: List of species node identifiers.
|
|
478
|
+
:type species_nodes: List[Hashable]
|
|
479
|
+
:param rule_nodes: List of rule node identifiers.
|
|
480
|
+
:type rule_nodes: List[Hashable]
|
|
481
|
+
:return: Selected layout name.
|
|
482
|
+
:rtype: str
|
|
483
|
+
"""
|
|
484
|
+
n_nodes = graph.number_of_nodes()
|
|
485
|
+
n_edges = graph.number_of_edges()
|
|
486
|
+
density = nx.density(graph) if n_nodes > 1 else 0.0
|
|
487
|
+
has_step = any(graph.nodes[node].get("step") is not None for node in rule_nodes)
|
|
488
|
+
|
|
489
|
+
if has_step and (n_nodes >= 35 or n_edges >= 55 or density >= 0.045):
|
|
490
|
+
return "multipartite_step"
|
|
491
|
+
if has_step:
|
|
492
|
+
return "step"
|
|
493
|
+
if n_nodes >= 40 or density >= 0.06:
|
|
494
|
+
return "kamada_kawai"
|
|
495
|
+
return "spring"
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def _spring_layout(
|
|
499
|
+
graph: nx.DiGraph,
|
|
500
|
+
*,
|
|
501
|
+
seed: int,
|
|
502
|
+
**_: object,
|
|
503
|
+
) -> Pos:
|
|
504
|
+
"""Compute a spring layout with a slightly enlarged optimal distance.
|
|
505
|
+
|
|
506
|
+
:param graph: Directed graph to layout.
|
|
507
|
+
:type graph: nx.DiGraph
|
|
508
|
+
:param seed: Random seed for the spring layout.
|
|
509
|
+
:type seed: int
|
|
510
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
511
|
+
:rtype: Pos
|
|
512
|
+
"""
|
|
513
|
+
n_nodes = max(graph.number_of_nodes(), 1)
|
|
514
|
+
k = 1.8 / (n_nodes**0.5)
|
|
515
|
+
return nx.spring_layout(graph, seed=seed, k=k, iterations=200)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _kamada_kawai_layout(
|
|
519
|
+
graph: nx.DiGraph,
|
|
520
|
+
**_: object,
|
|
521
|
+
) -> Pos:
|
|
522
|
+
"""Compute a Kamada-Kawai layout.
|
|
523
|
+
|
|
524
|
+
:param graph: Directed graph to layout.
|
|
525
|
+
:type graph: nx.DiGraph
|
|
526
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
527
|
+
:rtype: Pos
|
|
528
|
+
"""
|
|
529
|
+
return nx.kamada_kawai_layout(graph)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _spectral_layout(
|
|
533
|
+
graph: nx.DiGraph,
|
|
534
|
+
**_: object,
|
|
535
|
+
) -> Pos:
|
|
536
|
+
"""Compute a spectral layout.
|
|
537
|
+
|
|
538
|
+
:param graph: Directed graph to layout.
|
|
539
|
+
:type graph: nx.DiGraph
|
|
540
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
541
|
+
:rtype: Pos
|
|
542
|
+
"""
|
|
543
|
+
return nx.spectral_layout(graph)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def _shell_layout(
|
|
547
|
+
graph: nx.DiGraph,
|
|
548
|
+
*,
|
|
549
|
+
species_nodes: List[Hashable],
|
|
550
|
+
rule_nodes: List[Hashable],
|
|
551
|
+
**_: object,
|
|
552
|
+
) -> Pos:
|
|
553
|
+
"""Compute a shell layout with species and rule nodes grouped into shells.
|
|
554
|
+
|
|
555
|
+
:param graph: Directed graph to layout.
|
|
556
|
+
:type graph: nx.DiGraph
|
|
557
|
+
:param species_nodes: List of species node identifiers.
|
|
558
|
+
:type species_nodes: List[Hashable]
|
|
559
|
+
:param rule_nodes: List of rule node identifiers.
|
|
560
|
+
:type rule_nodes: List[Hashable]
|
|
561
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
562
|
+
:rtype: Pos
|
|
563
|
+
"""
|
|
564
|
+
shells: List[List[Hashable]] = []
|
|
565
|
+
if species_nodes:
|
|
566
|
+
shells.append(species_nodes)
|
|
567
|
+
if rule_nodes:
|
|
568
|
+
shells.append(rule_nodes)
|
|
569
|
+
return nx.shell_layout(graph, nlist=shells) if shells else nx.shell_layout(graph)
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def _spiral_layout(
|
|
573
|
+
graph: nx.DiGraph,
|
|
574
|
+
**_: object,
|
|
575
|
+
) -> Pos:
|
|
576
|
+
"""Compute a spiral layout.
|
|
577
|
+
|
|
578
|
+
:param graph: Directed graph to layout.
|
|
579
|
+
:type graph: nx.DiGraph
|
|
580
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
581
|
+
:rtype: Pos
|
|
582
|
+
"""
|
|
583
|
+
return nx.spiral_layout(graph)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _random_layout(
|
|
587
|
+
graph: nx.DiGraph,
|
|
588
|
+
*,
|
|
589
|
+
seed: int,
|
|
590
|
+
**_: object,
|
|
591
|
+
) -> Pos:
|
|
592
|
+
"""Compute a random layout.
|
|
593
|
+
|
|
594
|
+
:param graph: Directed graph to layout.
|
|
595
|
+
:type graph: nx.DiGraph
|
|
596
|
+
:param seed: Random seed for layout generation.
|
|
597
|
+
:type seed: int
|
|
598
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
599
|
+
:rtype: Pos
|
|
600
|
+
"""
|
|
601
|
+
return nx.random_layout(graph, seed=seed)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
_LAYOUT_REGISTRY: Mapping[str, LayoutFunc] = {
|
|
605
|
+
"step": step_layout,
|
|
606
|
+
"bipartite": bipartite_layout,
|
|
607
|
+
"multipartite_step": multipartite_step_layout,
|
|
608
|
+
"radial_step": radial_step_layout,
|
|
609
|
+
"circular_bipartite": circular_bipartite_layout,
|
|
610
|
+
"degree_shell": degree_shell_layout,
|
|
611
|
+
"spring": _spring_layout,
|
|
612
|
+
"kamada_kawai": _kamada_kawai_layout,
|
|
613
|
+
"spectral": _spectral_layout,
|
|
614
|
+
"shell": _shell_layout,
|
|
615
|
+
"spiral": _spiral_layout,
|
|
616
|
+
"random": _random_layout,
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def available_layouts() -> List[str]:
|
|
621
|
+
"""Return the list of supported layout names.
|
|
622
|
+
|
|
623
|
+
:return: Supported layout names, including ``"auto"``.
|
|
624
|
+
:rtype: List[str]
|
|
625
|
+
|
|
626
|
+
.. code-block:: python
|
|
627
|
+
|
|
628
|
+
names = available_layouts()
|
|
629
|
+
print(names)
|
|
630
|
+
"""
|
|
631
|
+
return ["auto", *_LAYOUT_REGISTRY.keys()]
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def compute_layout(
|
|
635
|
+
graph: nx.DiGraph,
|
|
636
|
+
*,
|
|
637
|
+
species_nodes: List[Hashable],
|
|
638
|
+
rule_nodes: List[Hashable],
|
|
639
|
+
layout: str = "step",
|
|
640
|
+
node_spacing: float = 1.4,
|
|
641
|
+
layer_spacing: float = 2.5,
|
|
642
|
+
seed: int = 0,
|
|
643
|
+
orientation: str = "vertical",
|
|
644
|
+
) -> Pos:
|
|
645
|
+
"""Compute node positions for CRN visualization.
|
|
646
|
+
|
|
647
|
+
:param graph: Directed graph containing species and rule nodes.
|
|
648
|
+
:type graph: nx.DiGraph
|
|
649
|
+
:param species_nodes: List of species node identifiers.
|
|
650
|
+
:type species_nodes: List[Hashable]
|
|
651
|
+
:param rule_nodes: List of rule node identifiers.
|
|
652
|
+
:type rule_nodes: List[Hashable]
|
|
653
|
+
:param layout: Layout name. Supported values are ``"auto"``, ``"step"``,
|
|
654
|
+
``"bipartite"``, ``"multipartite_step"``, ``"radial_step"``,
|
|
655
|
+
``"circular_bipartite"``, ``"degree_shell"``, ``"spring"``,
|
|
656
|
+
``"kamada_kawai"``, ``"spectral"``, ``"shell"``, ``"spiral"``,
|
|
657
|
+
and ``"random"``.
|
|
658
|
+
:type layout: str
|
|
659
|
+
:param node_spacing: Spacing between nodes within a layer for layouts that
|
|
660
|
+
support it.
|
|
661
|
+
:type node_spacing: float
|
|
662
|
+
:param layer_spacing: Spacing between layers for layouts that support it.
|
|
663
|
+
:type layer_spacing: float
|
|
664
|
+
:param seed: Random seed for stochastic layouts.
|
|
665
|
+
:type seed: int
|
|
666
|
+
:param orientation: Orientation for the bipartite layout. Must be
|
|
667
|
+
``"vertical"`` or ``"horizontal"``.
|
|
668
|
+
:type orientation: str
|
|
669
|
+
:return: Mapping from node identifier to ``(x, y)`` coordinates.
|
|
670
|
+
:rtype: Pos
|
|
671
|
+
:raises ValueError: If the requested layout name is not supported.
|
|
672
|
+
|
|
673
|
+
.. code-block:: python
|
|
674
|
+
|
|
675
|
+
pos = compute_layout(
|
|
676
|
+
graph,
|
|
677
|
+
species_nodes=species_nodes,
|
|
678
|
+
rule_nodes=rule_nodes,
|
|
679
|
+
layout="auto",
|
|
680
|
+
)
|
|
681
|
+
"""
|
|
682
|
+
resolved_layout = (
|
|
683
|
+
choose_auto_layout(
|
|
684
|
+
graph,
|
|
685
|
+
species_nodes=species_nodes,
|
|
686
|
+
rule_nodes=rule_nodes,
|
|
687
|
+
)
|
|
688
|
+
if layout == "auto"
|
|
689
|
+
else layout
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
try:
|
|
693
|
+
layout_func = _LAYOUT_REGISTRY[resolved_layout]
|
|
694
|
+
except KeyError as exc:
|
|
695
|
+
supported = ", ".join(repr(name) for name in available_layouts())
|
|
696
|
+
raise ValueError(
|
|
697
|
+
f"Unsupported layout {layout!r}. Use one of: {supported}."
|
|
698
|
+
) from exc
|
|
699
|
+
|
|
700
|
+
return layout_func(
|
|
701
|
+
graph,
|
|
702
|
+
species_nodes=species_nodes,
|
|
703
|
+
rule_nodes=rule_nodes,
|
|
704
|
+
node_spacing=node_spacing,
|
|
705
|
+
layer_spacing=layer_spacing,
|
|
706
|
+
seed=seed,
|
|
707
|
+
orientation=orientation,
|
|
708
|
+
)
|