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,520 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Representation-aware visual adapters for SynKit graphs.
|
|
4
|
+
|
|
5
|
+
This module is intentionally lightweight: it detects the graph representation
|
|
6
|
+
and converts raw NetworkX attributes into stable labels/colors that drawing
|
|
7
|
+
backends can consume. The adapters never mutate the input graph.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Any, Dict, Hashable, Iterable, Literal, Mapping
|
|
12
|
+
|
|
13
|
+
import networkx as nx
|
|
14
|
+
|
|
15
|
+
VisualKind = Literal[
|
|
16
|
+
"molecule",
|
|
17
|
+
"legacy_its",
|
|
18
|
+
"tuple_its",
|
|
19
|
+
"compact_mtg",
|
|
20
|
+
"mechanism_dag",
|
|
21
|
+
"unknown",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class VisualNode:
|
|
27
|
+
"""Drawing-ready node information."""
|
|
28
|
+
|
|
29
|
+
node_id: Hashable
|
|
30
|
+
label: str
|
|
31
|
+
element: str | None = None
|
|
32
|
+
atom_map: int | None = None
|
|
33
|
+
badges: tuple[str, ...] = ()
|
|
34
|
+
changed: bool = False
|
|
35
|
+
raw: Mapping[str, Any] = field(default_factory=dict)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class VisualEdge:
|
|
40
|
+
"""Drawing-ready edge information."""
|
|
41
|
+
|
|
42
|
+
source: Hashable
|
|
43
|
+
target: Hashable
|
|
44
|
+
label: str = ""
|
|
45
|
+
state: str = "unchanged"
|
|
46
|
+
color: str = "#2f3437"
|
|
47
|
+
width: float = 2.0
|
|
48
|
+
raw: Mapping[str, Any] = field(default_factory=dict)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class VisualGraph:
|
|
53
|
+
"""A compact, immutable view of graph content for renderers."""
|
|
54
|
+
|
|
55
|
+
kind: VisualKind
|
|
56
|
+
nodes: tuple[VisualNode, ...]
|
|
57
|
+
edges: tuple[VisualEdge, ...]
|
|
58
|
+
title: str = ""
|
|
59
|
+
metadata: Mapping[str, Any] = field(default_factory=dict)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
BOND_SYMBOLS = {
|
|
63
|
+
None: "∅",
|
|
64
|
+
0: "∅",
|
|
65
|
+
0.0: "∅",
|
|
66
|
+
1: "—",
|
|
67
|
+
1.0: "—",
|
|
68
|
+
1.5: ":",
|
|
69
|
+
2: "=",
|
|
70
|
+
2.0: "=",
|
|
71
|
+
3: "≡",
|
|
72
|
+
3.0: "≡",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
EDGE_COLORS = {
|
|
76
|
+
"unchanged": "#6b7280",
|
|
77
|
+
"formed": "#15803d",
|
|
78
|
+
"broken": "#b91c1c",
|
|
79
|
+
"order_changed": "#ca8a04",
|
|
80
|
+
"transient": "#7c3aed",
|
|
81
|
+
"unknown": "#2f3437",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
NODE_TIMELINE_ATTRS = (
|
|
85
|
+
"aromatic",
|
|
86
|
+
"hcount",
|
|
87
|
+
"charge",
|
|
88
|
+
"radical",
|
|
89
|
+
"lone_pairs",
|
|
90
|
+
"present",
|
|
91
|
+
)
|
|
92
|
+
EDGE_TIMELINE_ATTRS = ("order", "kekule_order", "sigma_order", "pi_order")
|
|
93
|
+
ELECTRON_NODE_ATTRS = ("charge", "hcount", "lone_pairs", "radical")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def detect_visual_kind(graph: nx.Graph) -> VisualKind:
|
|
97
|
+
"""Return the visualization representation kind for ``graph``.
|
|
98
|
+
|
|
99
|
+
:param graph: NetworkX graph to inspect.
|
|
100
|
+
:type graph: nx.Graph
|
|
101
|
+
:return: Detected graph kind.
|
|
102
|
+
:rtype: VisualKind
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
if not isinstance(graph, nx.Graph):
|
|
106
|
+
return "unknown"
|
|
107
|
+
|
|
108
|
+
if _looks_like_mechanism_dag(graph):
|
|
109
|
+
return "mechanism_dag"
|
|
110
|
+
if _looks_like_compact_mtg(graph):
|
|
111
|
+
return "compact_mtg"
|
|
112
|
+
if _looks_like_tuple_its(graph):
|
|
113
|
+
return "tuple_its"
|
|
114
|
+
if _looks_like_legacy_its(graph):
|
|
115
|
+
return "legacy_its"
|
|
116
|
+
if _looks_like_molecule(graph):
|
|
117
|
+
return "molecule"
|
|
118
|
+
return "unknown"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def to_visual_graph(
|
|
122
|
+
graph: nx.Graph,
|
|
123
|
+
*,
|
|
124
|
+
kind: VisualKind | None = None,
|
|
125
|
+
mode: Literal["compact", "electron", "sigma_pi", "timeline"] = "compact",
|
|
126
|
+
show_atom_map: bool = True,
|
|
127
|
+
title: str = "",
|
|
128
|
+
) -> VisualGraph:
|
|
129
|
+
"""Adapt a SynKit graph to drawing-ready labels.
|
|
130
|
+
|
|
131
|
+
:param graph: NetworkX graph to adapt.
|
|
132
|
+
:type graph: nx.Graph
|
|
133
|
+
:param kind: Optional explicit representation kind.
|
|
134
|
+
:type kind: Optional[VisualKind]
|
|
135
|
+
:param mode: Label density. ``sigma_pi`` and ``timeline`` are mostly useful
|
|
136
|
+
for tuple ITS and compact MTG.
|
|
137
|
+
:type mode: str
|
|
138
|
+
:param show_atom_map: Include atom-map numbers in node labels when present.
|
|
139
|
+
:type show_atom_map: bool
|
|
140
|
+
:param title: Optional title carried to renderer metadata.
|
|
141
|
+
:type title: str
|
|
142
|
+
:return: Immutable visual graph model.
|
|
143
|
+
:rtype: VisualGraph
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
detected = kind or detect_visual_kind(graph)
|
|
147
|
+
nodes = tuple(
|
|
148
|
+
_adapt_node(node_id, attrs, detected, mode=mode, show_atom_map=show_atom_map)
|
|
149
|
+
for node_id, attrs in graph.nodes(data=True)
|
|
150
|
+
)
|
|
151
|
+
edges = tuple(
|
|
152
|
+
_adapt_edge(u, v, attrs, detected, mode=mode)
|
|
153
|
+
for u, v, attrs in graph.edges(data=True)
|
|
154
|
+
)
|
|
155
|
+
return VisualGraph(
|
|
156
|
+
kind=detected,
|
|
157
|
+
nodes=nodes,
|
|
158
|
+
edges=edges,
|
|
159
|
+
title=title,
|
|
160
|
+
metadata={
|
|
161
|
+
"mode": mode,
|
|
162
|
+
"node_count": graph.number_of_nodes(),
|
|
163
|
+
"edge_count": graph.number_of_edges(),
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def summarize_visual_graph(visual: VisualGraph) -> Dict[str, Any]:
|
|
169
|
+
"""Return a notebook-friendly summary of a visual graph."""
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
"kind": visual.kind,
|
|
173
|
+
"title": visual.title,
|
|
174
|
+
"metadata": dict(visual.metadata),
|
|
175
|
+
"nodes": [
|
|
176
|
+
{
|
|
177
|
+
"id": node.node_id,
|
|
178
|
+
"label": node.label,
|
|
179
|
+
"badges": list(node.badges),
|
|
180
|
+
"changed": node.changed,
|
|
181
|
+
}
|
|
182
|
+
for node in visual.nodes
|
|
183
|
+
],
|
|
184
|
+
"edges": [
|
|
185
|
+
{
|
|
186
|
+
"source": edge.source,
|
|
187
|
+
"target": edge.target,
|
|
188
|
+
"label": edge.label,
|
|
189
|
+
"state": edge.state,
|
|
190
|
+
"color": edge.color,
|
|
191
|
+
}
|
|
192
|
+
for edge in visual.edges
|
|
193
|
+
],
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _looks_like_molecule(graph: nx.Graph) -> bool:
|
|
198
|
+
return bool(graph.nodes) and all(
|
|
199
|
+
"element" in attrs and not _is_pair(attrs.get("element"))
|
|
200
|
+
for _, attrs in graph.nodes(data=True)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _looks_like_legacy_its(graph: nx.Graph) -> bool:
|
|
205
|
+
if not graph.edges:
|
|
206
|
+
return False
|
|
207
|
+
has_pair_order = any(
|
|
208
|
+
_is_pair(attrs.get("order")) for _, _, attrs in graph.edges(data=True)
|
|
209
|
+
)
|
|
210
|
+
has_tuple_electron_edges = any(
|
|
211
|
+
key in attrs
|
|
212
|
+
for _, _, attrs in graph.edges(data=True)
|
|
213
|
+
for key in ("sigma_order", "pi_order", "kekule_order")
|
|
214
|
+
)
|
|
215
|
+
return has_pair_order and not has_tuple_electron_edges
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _looks_like_tuple_its(graph: nx.Graph) -> bool:
|
|
219
|
+
if not graph.nodes and not graph.edges:
|
|
220
|
+
return False
|
|
221
|
+
node_pair = any(
|
|
222
|
+
_is_pair(attrs.get(key))
|
|
223
|
+
for _, attrs in graph.nodes(data=True)
|
|
224
|
+
for key in ("element", "hcount", "charge", "lone_pairs", "radical", "present")
|
|
225
|
+
)
|
|
226
|
+
edge_pair = any(
|
|
227
|
+
_is_pair(attrs.get(key))
|
|
228
|
+
for _, _, attrs in graph.edges(data=True)
|
|
229
|
+
for key in ("sigma_order", "pi_order", "kekule_order")
|
|
230
|
+
)
|
|
231
|
+
return node_pair or edge_pair
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _looks_like_compact_mtg(graph: nx.Graph) -> bool:
|
|
235
|
+
graph_steps = graph.graph.get("steps")
|
|
236
|
+
if isinstance(graph_steps, int) and graph_steps >= 2:
|
|
237
|
+
return True
|
|
238
|
+
has_steps_attr = any(
|
|
239
|
+
"steps" in attrs for _, attrs in graph.nodes(data=True)
|
|
240
|
+
) or any("steps" in attrs for _, _, attrs in graph.edges(data=True))
|
|
241
|
+
if not has_steps_attr:
|
|
242
|
+
return False
|
|
243
|
+
has_timeline = any(
|
|
244
|
+
_is_timeline(attrs.get(key))
|
|
245
|
+
for _, attrs in graph.nodes(data=True)
|
|
246
|
+
for key in NODE_TIMELINE_ATTRS
|
|
247
|
+
) or any(
|
|
248
|
+
_is_timeline(attrs.get(key))
|
|
249
|
+
for _, _, attrs in graph.edges(data=True)
|
|
250
|
+
for key in EDGE_TIMELINE_ATTRS
|
|
251
|
+
)
|
|
252
|
+
return has_timeline
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _looks_like_mechanism_dag(graph: nx.Graph) -> bool:
|
|
256
|
+
if not graph.is_directed():
|
|
257
|
+
return False
|
|
258
|
+
graph_kind = str(graph.graph.get("kind", "")).lower()
|
|
259
|
+
if graph_kind in {"mechanism_dag", "mechanism", "reaction_dag"}:
|
|
260
|
+
return True
|
|
261
|
+
return any(
|
|
262
|
+
attrs.get("kind") in {"reaction", "step", "species"}
|
|
263
|
+
for _, attrs in graph.nodes(data=True)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _adapt_node(
|
|
268
|
+
node_id: Hashable,
|
|
269
|
+
attrs: Mapping[str, Any],
|
|
270
|
+
kind: VisualKind,
|
|
271
|
+
*,
|
|
272
|
+
mode: str,
|
|
273
|
+
show_atom_map: bool,
|
|
274
|
+
) -> VisualNode:
|
|
275
|
+
element = _first_present(attrs.get("element"))
|
|
276
|
+
atom_map = _first_present(attrs.get("atom_map"))
|
|
277
|
+
label = str(element or node_id)
|
|
278
|
+
if show_atom_map and atom_map not in (None, 0):
|
|
279
|
+
label = f"{label}:{atom_map}"
|
|
280
|
+
elif show_atom_map and atom_map == 0:
|
|
281
|
+
label = f"{label}:{node_id}"
|
|
282
|
+
|
|
283
|
+
badges = _node_badges(attrs, kind, mode)
|
|
284
|
+
changed = bool(badges) or any(
|
|
285
|
+
_is_changed_pair(attrs.get(key)) for key in ELECTRON_NODE_ATTRS
|
|
286
|
+
)
|
|
287
|
+
return VisualNode(
|
|
288
|
+
node_id=node_id,
|
|
289
|
+
label=label,
|
|
290
|
+
element=str(element) if element is not None else None,
|
|
291
|
+
atom_map=int(atom_map) if isinstance(atom_map, int) else None,
|
|
292
|
+
badges=tuple(badges),
|
|
293
|
+
changed=changed,
|
|
294
|
+
raw=dict(attrs),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _adapt_edge(
|
|
299
|
+
u: Hashable,
|
|
300
|
+
v: Hashable,
|
|
301
|
+
attrs: Mapping[str, Any],
|
|
302
|
+
kind: VisualKind,
|
|
303
|
+
*,
|
|
304
|
+
mode: str,
|
|
305
|
+
) -> VisualEdge:
|
|
306
|
+
if kind == "compact_mtg":
|
|
307
|
+
label = _mtg_edge_label(attrs, mode)
|
|
308
|
+
state = _timeline_edge_state(attrs)
|
|
309
|
+
elif kind == "tuple_its":
|
|
310
|
+
label = _tuple_its_edge_label(attrs, mode)
|
|
311
|
+
state = _pair_edge_state(_preferred_edge_pair(attrs))
|
|
312
|
+
elif kind == "legacy_its":
|
|
313
|
+
pair = attrs.get("order", (None, None))
|
|
314
|
+
label = _pair_label(pair)
|
|
315
|
+
state = _pair_edge_state(pair)
|
|
316
|
+
else:
|
|
317
|
+
order = attrs.get("order")
|
|
318
|
+
label = _bond_symbol(order)
|
|
319
|
+
state = "unchanged"
|
|
320
|
+
|
|
321
|
+
return VisualEdge(
|
|
322
|
+
source=u,
|
|
323
|
+
target=v,
|
|
324
|
+
label=label,
|
|
325
|
+
state=state,
|
|
326
|
+
color=EDGE_COLORS.get(state, EDGE_COLORS["unknown"]),
|
|
327
|
+
width=(
|
|
328
|
+
3.0 if state in {"formed", "broken", "order_changed", "transient"} else 2.0
|
|
329
|
+
),
|
|
330
|
+
raw=dict(attrs),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _node_badges(attrs: Mapping[str, Any], kind: VisualKind, mode: str) -> list[str]:
|
|
335
|
+
badges: list[str] = []
|
|
336
|
+
if kind in {"tuple_its", "compact_mtg"}:
|
|
337
|
+
for key in ELECTRON_NODE_ATTRS:
|
|
338
|
+
value = attrs.get(key)
|
|
339
|
+
if kind == "compact_mtg" and _is_timeline(value):
|
|
340
|
+
if _timeline_changes(value) or mode in {"electron", "timeline"}:
|
|
341
|
+
badges.append(f"{_short_node_key(key)}:{_format_timeline(value)}")
|
|
342
|
+
elif _is_pair(value):
|
|
343
|
+
if value[0] != value[1] or mode in {"electron", "timeline"}:
|
|
344
|
+
badges.append(f"{_short_node_key(key)}:{_format_pair(value)}")
|
|
345
|
+
elif mode in {"electron", "timeline"} and value not in (None, 0, False):
|
|
346
|
+
badges.append(f"{_short_node_key(key)}:{value}")
|
|
347
|
+
elif kind == "molecule":
|
|
348
|
+
for key in ("charge", "radical", "lone_pairs"):
|
|
349
|
+
value = attrs.get(key)
|
|
350
|
+
if value not in (None, 0, False):
|
|
351
|
+
badges.append(f"{_short_node_key(key)}:{value}")
|
|
352
|
+
return badges
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _tuple_its_edge_label(attrs: Mapping[str, Any], mode: str) -> str:
|
|
356
|
+
if mode == "sigma_pi":
|
|
357
|
+
sigma = attrs.get("sigma_order")
|
|
358
|
+
pi = attrs.get("pi_order")
|
|
359
|
+
return f"σ{_format_pair(sigma)} π{_format_pair(pi)}"
|
|
360
|
+
pair = attrs.get("kekule_order", attrs.get("order"))
|
|
361
|
+
return _pair_label(pair)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _mtg_edge_label(attrs: Mapping[str, Any], mode: str) -> str:
|
|
365
|
+
if mode == "sigma_pi":
|
|
366
|
+
parts = []
|
|
367
|
+
for key in ("sigma_order", "pi_order"):
|
|
368
|
+
value = attrs.get(key)
|
|
369
|
+
if _is_timeline(value) and (_timeline_changes(value) or mode == "timeline"):
|
|
370
|
+
parts.append(f"{_short_edge_key(key)}:{_format_timeline(value)}")
|
|
371
|
+
return " ".join(parts) or _format_timeline(
|
|
372
|
+
attrs.get("kekule_order", attrs.get("order"))
|
|
373
|
+
)
|
|
374
|
+
if mode == "timeline":
|
|
375
|
+
parts = []
|
|
376
|
+
for key in EDGE_TIMELINE_ATTRS:
|
|
377
|
+
value = attrs.get(key)
|
|
378
|
+
if _is_timeline(value):
|
|
379
|
+
parts.append(f"{_short_edge_key(key)}:{_format_timeline(value)}")
|
|
380
|
+
return " ".join(parts)
|
|
381
|
+
value = attrs.get("kekule_order", attrs.get("order"))
|
|
382
|
+
return _format_timeline(value) if _is_timeline(value) else _bond_symbol(value)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _preferred_edge_pair(attrs: Mapping[str, Any]) -> Any:
|
|
386
|
+
return attrs.get("kekule_order", attrs.get("order"))
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _pair_edge_state(pair: Any) -> str:
|
|
390
|
+
if not _is_pair(pair):
|
|
391
|
+
return "unknown"
|
|
392
|
+
before, after = pair
|
|
393
|
+
before = _none_order(before)
|
|
394
|
+
after = _none_order(after)
|
|
395
|
+
if before == after:
|
|
396
|
+
return "unchanged"
|
|
397
|
+
if before == 0 and after > 0:
|
|
398
|
+
return "formed"
|
|
399
|
+
if before > 0 and after == 0:
|
|
400
|
+
return "broken"
|
|
401
|
+
return "order_changed"
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _timeline_edge_state(attrs: Mapping[str, Any]) -> str:
|
|
405
|
+
timeline = attrs.get("kekule_order", attrs.get("order"))
|
|
406
|
+
if not _is_timeline(timeline):
|
|
407
|
+
return "unknown"
|
|
408
|
+
numeric = [_none_order(v) for v in timeline if _is_order_value(v)]
|
|
409
|
+
if not numeric or len(set(numeric)) == 1:
|
|
410
|
+
return "unchanged"
|
|
411
|
+
if numeric[0] == numeric[-1] and len(set(numeric)) > 1:
|
|
412
|
+
return "transient"
|
|
413
|
+
if numeric[0] == 0 and numeric[-1] > 0:
|
|
414
|
+
return "formed"
|
|
415
|
+
if numeric[0] > 0 and numeric[-1] == 0:
|
|
416
|
+
return "broken"
|
|
417
|
+
return "order_changed"
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _pair_label(pair: Any) -> str:
|
|
421
|
+
if not _is_pair(pair):
|
|
422
|
+
return _bond_symbol(pair)
|
|
423
|
+
return f"{_bond_symbol(pair[0])}>{_bond_symbol(pair[1])}"
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _format_pair(pair: Any) -> str:
|
|
427
|
+
if not _is_pair(pair):
|
|
428
|
+
return str(pair)
|
|
429
|
+
return f"{_format_value(pair[0])}>{_format_value(pair[1])}"
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _format_timeline(value: Any) -> str:
|
|
433
|
+
if not _is_timeline(value):
|
|
434
|
+
return str(value)
|
|
435
|
+
return "-".join(_format_value(item) for item in value)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _format_value(value: Any) -> str:
|
|
439
|
+
if value is None:
|
|
440
|
+
return "∅"
|
|
441
|
+
if isinstance(value, float) and value.is_integer():
|
|
442
|
+
return str(int(value))
|
|
443
|
+
return str(value)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _bond_symbol(order: Any) -> str:
|
|
447
|
+
return BOND_SYMBOLS.get(order, _format_value(order))
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _short_node_key(key: str) -> str:
|
|
451
|
+
return {
|
|
452
|
+
"charge": "q",
|
|
453
|
+
"hcount": "H",
|
|
454
|
+
"lone_pairs": "lp",
|
|
455
|
+
"radical": "rad",
|
|
456
|
+
}.get(key, key)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _short_edge_key(key: str) -> str:
|
|
460
|
+
return {
|
|
461
|
+
"order": "ord",
|
|
462
|
+
"kekule_order": "kek",
|
|
463
|
+
"sigma_order": "σ",
|
|
464
|
+
"pi_order": "π",
|
|
465
|
+
}.get(key, key)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _is_pair(value: Any) -> bool:
|
|
469
|
+
return (
|
|
470
|
+
isinstance(value, tuple)
|
|
471
|
+
and len(value) == 2
|
|
472
|
+
and not any(isinstance(item, (set, list, tuple, dict)) for item in value)
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _is_timeline(value: Any) -> bool:
|
|
477
|
+
return (
|
|
478
|
+
isinstance(value, tuple)
|
|
479
|
+
and len(value) >= 3
|
|
480
|
+
and not any(isinstance(item, (set, list, tuple, dict)) for item in value)
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _is_changed_pair(value: Any) -> bool:
|
|
485
|
+
return _is_pair(value) and value[0] != value[1]
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def _timeline_changes(value: Any) -> bool:
|
|
489
|
+
return _is_timeline(value) and len(set(value)) > 1
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def _is_order_value(value: Any) -> bool:
|
|
493
|
+
return value is None or isinstance(value, (int, float))
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _none_order(value: Any) -> float:
|
|
497
|
+
return 0.0 if value is None else float(value)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def _first_present(value: Any) -> Any:
|
|
501
|
+
if _is_pair(value):
|
|
502
|
+
return value[0] if value[0] is not None else value[1]
|
|
503
|
+
if _is_timeline(value):
|
|
504
|
+
for item in value:
|
|
505
|
+
if item is not None:
|
|
506
|
+
return item
|
|
507
|
+
return None
|
|
508
|
+
return value
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def iter_changed_edges(visual: VisualGraph) -> Iterable[VisualEdge]:
|
|
512
|
+
"""Yield edges whose visual state is not unchanged."""
|
|
513
|
+
|
|
514
|
+
return (edge for edge in visual.edges if edge.state != "unchanged")
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def iter_changed_nodes(visual: VisualGraph) -> Iterable[VisualNode]:
|
|
518
|
+
"""Yield nodes with at least one visual badge/change marker."""
|
|
519
|
+
|
|
520
|
+
return (node for node in visual.nodes if node.changed)
|
synkit/__init__.py
ADDED
synkit/_native.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Shared discovery and installed-file verification for bundled C ABI libraries."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
import hashlib
|
|
6
|
+
from importlib.metadata import distribution
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def bundled_library(package_dir, stem):
|
|
14
|
+
"""Locate one packaged library without compiling or downloading anything."""
|
|
15
|
+
name = (
|
|
16
|
+
stem + ".dll"
|
|
17
|
+
if sys.platform == "win32"
|
|
18
|
+
else (
|
|
19
|
+
"lib" + stem + ".dylib"
|
|
20
|
+
if sys.platform == "darwin"
|
|
21
|
+
else "lib" + stem + ".so"
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
path = Path(package_dir).resolve() / "lib" / name
|
|
25
|
+
if not path.is_file():
|
|
26
|
+
raise FileNotFoundError(f"No bundled {stem} library is installed")
|
|
27
|
+
return path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@lru_cache(maxsize=8)
|
|
31
|
+
def _conda_record_digest(library_file):
|
|
32
|
+
"""Read the owning Conda package's checksum after prefix relocation."""
|
|
33
|
+
package = Path(__file__).resolve().parent
|
|
34
|
+
library_file.relative_to(package)
|
|
35
|
+
for prefix in package.parents:
|
|
36
|
+
metadata = prefix / "conda-meta"
|
|
37
|
+
if metadata.is_dir():
|
|
38
|
+
return _conda_prefix_digest(prefix, library_file)
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _conda_prefix_digest(prefix, library_file):
|
|
43
|
+
relative = os.path.normcase(library_file.relative_to(prefix).as_posix())
|
|
44
|
+
for filename in sorted((prefix / "conda-meta").glob("synkit-*.json")):
|
|
45
|
+
try:
|
|
46
|
+
record = json.loads(filename.read_text(encoding="utf-8"))
|
|
47
|
+
except (OSError, ValueError) as exc:
|
|
48
|
+
raise RuntimeError("Cannot read installed SynKit Conda metadata") from exc
|
|
49
|
+
if record.get("name") != "synkit":
|
|
50
|
+
continue
|
|
51
|
+
for entry in record.get("paths_data", {}).get("paths", ()):
|
|
52
|
+
if os.path.normcase(entry.get("_path", "")) != relative:
|
|
53
|
+
continue
|
|
54
|
+
# Conda and Mamba record bytes after relocation/code signing here.
|
|
55
|
+
digest = entry.get("sha256_in_prefix") or entry.get("sha256")
|
|
56
|
+
if (
|
|
57
|
+
isinstance(digest, str)
|
|
58
|
+
and len(digest) == 64
|
|
59
|
+
and all(c in "0123456789abcdefABCDEF" for c in digest)
|
|
60
|
+
):
|
|
61
|
+
return digest.lower()
|
|
62
|
+
raise RuntimeError(f"The bundled library has no installed Conda SHA-256 record: {relative}")
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@lru_cache(maxsize=8)
|
|
67
|
+
def _wheel_record_digest(library_file):
|
|
68
|
+
installed = distribution("synkit")
|
|
69
|
+
relative = (
|
|
70
|
+
"synkit/" + library_file.relative_to(Path(__file__).resolve().parent).as_posix()
|
|
71
|
+
)
|
|
72
|
+
for entry in installed.files or ():
|
|
73
|
+
if (
|
|
74
|
+
str(entry) == relative
|
|
75
|
+
and Path(installed.locate_file(entry)).resolve() == library_file
|
|
76
|
+
):
|
|
77
|
+
if entry.hash is not None and entry.hash.mode == "sha256":
|
|
78
|
+
return base64.urlsafe_b64decode(entry.hash.value + "==").hex()
|
|
79
|
+
raise RuntimeError(
|
|
80
|
+
f"The bundled library has no installed wheel SHA-256 record: {relative}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def verify_wheel_library(path):
|
|
85
|
+
"""Check final installed bytes against their Conda or wheel SHA-256 record."""
|
|
86
|
+
path = Path(path).resolve()
|
|
87
|
+
expected = _conda_record_digest(path) or _wheel_record_digest(path)
|
|
88
|
+
actual = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
89
|
+
if actual != expected:
|
|
90
|
+
raise RuntimeError(f"Bundled native library checksum mismatch: {path.name}")
|
|
91
|
+
return actual
|
synkit/examples.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# synkit/examples.py
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from importlib.resources import files, as_file
|
|
7
|
+
|
|
8
|
+
from synkit.IO import load_database # adjust if the import path differs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _sanitize_slug(slug: str) -> str:
|
|
12
|
+
if ".." in slug or slug.startswith(("/", "\\")):
|
|
13
|
+
raise ValueError(f"Invalid example name: {slug!r}")
|
|
14
|
+
return slug
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def list_examples() -> list[str]:
|
|
18
|
+
"""
|
|
19
|
+
Returns all available example slugs (without extensions), e.g. ['paracetamol', ...]
|
|
20
|
+
"""
|
|
21
|
+
data_dir = files("synkit").joinpath("Data")
|
|
22
|
+
slugs: set[str] = set()
|
|
23
|
+
for p in data_dir.iterdir():
|
|
24
|
+
if not p.is_file():
|
|
25
|
+
continue
|
|
26
|
+
if p.name.endswith(".json.gz"):
|
|
27
|
+
slugs.add(p.name[: -len(".json.gz")])
|
|
28
|
+
elif p.name.endswith(".json"):
|
|
29
|
+
slugs.add(p.name[: -len(".json")])
|
|
30
|
+
return sorted(slugs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@lru_cache(maxsize=32)
|
|
34
|
+
def load_example(slug: str):
|
|
35
|
+
"""
|
|
36
|
+
Load an example by slug, preferring compressed (.json.gz) over plain (.json).
|
|
37
|
+
Delegates actual parsing to synkit.IO.load_database.
|
|
38
|
+
"""
|
|
39
|
+
slug = _sanitize_slug(slug)
|
|
40
|
+
data_dir = files("synkit").joinpath("Data")
|
|
41
|
+
candidates = [f"{slug}.json.gz", f"{slug}.json"]
|
|
42
|
+
for name in candidates:
|
|
43
|
+
resource = data_dir.joinpath(name)
|
|
44
|
+
if resource.is_file():
|
|
45
|
+
# importlib.resources may keep resources inside archives; as_file gives a real path
|
|
46
|
+
with as_file(resource) as real_path:
|
|
47
|
+
return load_database(real_path)
|
|
48
|
+
raise FileNotFoundError(
|
|
49
|
+
f"Example '{slug}' not found (looked for {', '.join(candidates)})"
|
|
50
|
+
)
|
synkit/version.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Installed and source-checkout version discovery."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import tomllib
|
|
8
|
+
except ModuleNotFoundError:
|
|
9
|
+
import tomli as tomllib
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
# A source checkout can coexist with an older installed distribution.
|
|
13
|
+
# Prefer its adjacent project metadata; installed wheels do not include
|
|
14
|
+
# the repository-level pyproject and therefore fall through naturally.
|
|
15
|
+
pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
16
|
+
__version__ = tomllib.loads(pyproject.read_text(encoding="utf-8"))["project"][
|
|
17
|
+
"version"
|
|
18
|
+
]
|
|
19
|
+
except (KeyError, OSError, tomllib.TOMLDecodeError):
|
|
20
|
+
try:
|
|
21
|
+
__version__ = version("synkit")
|
|
22
|
+
except PackageNotFoundError:
|
|
23
|
+
__version__ = "1.6.5"
|