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,349 @@
|
|
|
1
|
+
"""Immutable graph-morphism values and their composition laws."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Hashable, Mapping
|
|
8
|
+
|
|
9
|
+
from .constraints import ConstraintIssue, WildcardConstraint
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MorphismIssueCode(str, Enum):
|
|
13
|
+
"""Stable failure codes for morphism construction and composition."""
|
|
14
|
+
|
|
15
|
+
DUPLICATE_SOURCE = "MORPHISM_DUPLICATE_SOURCE"
|
|
16
|
+
NON_INJECTIVE = "MORPHISM_NON_INJECTIVE"
|
|
17
|
+
OUTSIDE_SOURCE = "MORPHISM_OUTSIDE_SOURCE"
|
|
18
|
+
OUTSIDE_TARGET = "MORPHISM_OUTSIDE_TARGET"
|
|
19
|
+
PARTIAL_MAPPING = "MORPHISM_PARTIAL_MAPPING"
|
|
20
|
+
ENDPOINT_MISMATCH = "MORPHISM_ENDPOINT_MISMATCH"
|
|
21
|
+
MISSING_INTERMEDIATE = "MORPHISM_MISSING_INTERMEDIATE"
|
|
22
|
+
CONSTRAINT_CONTRADICTION = "MORPHISM_CONSTRAINT_CONTRADICTION"
|
|
23
|
+
OWNER_OUTSIDE_SOURCE = "MORPHISM_OWNER_OUTSIDE_SOURCE"
|
|
24
|
+
OWNER_OUTSIDE_IMAGE = "MORPHISM_OWNER_OUTSIDE_IMAGE"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class MorphismIssue:
|
|
29
|
+
"""One construction or composition failure."""
|
|
30
|
+
|
|
31
|
+
code: MorphismIssueCode
|
|
32
|
+
message: str
|
|
33
|
+
context: Mapping[str, Any] = field(default_factory=dict)
|
|
34
|
+
constraint_issues: tuple[ConstraintIssue, ...] = ()
|
|
35
|
+
|
|
36
|
+
def to_dict(self) -> dict[str, Any]:
|
|
37
|
+
return {
|
|
38
|
+
"code": self.code.value,
|
|
39
|
+
"message": self.message,
|
|
40
|
+
"context": dict(self.context),
|
|
41
|
+
"constraint_issues": [issue.to_dict() for issue in self.constraint_issues],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class GraphMorphismError(ValueError):
|
|
46
|
+
"""Raised when an invalid morphism would be created or composed."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, *issues: MorphismIssue):
|
|
49
|
+
self.issues = tuple(issues)
|
|
50
|
+
message = "; ".join(issue.message for issue in self.issues)
|
|
51
|
+
super().__init__(message)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _pairs(value: Any) -> tuple[tuple[Hashable, Any], ...]:
|
|
55
|
+
if isinstance(value, Mapping):
|
|
56
|
+
return tuple(value.items())
|
|
57
|
+
return tuple(value)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _sorted_pairs(
|
|
61
|
+
value: tuple[tuple[Hashable, Any], ...],
|
|
62
|
+
) -> tuple[tuple[Hashable, Any], ...]:
|
|
63
|
+
return tuple(sorted(value, key=lambda item: (repr(item[0]), repr(item[1]))))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class GraphMorphism:
|
|
68
|
+
"""An injective embedding ``f`` with wildcard environment ``theta``.
|
|
69
|
+
|
|
70
|
+
``source`` and ``target`` identify endpoint objects. ``source_nodes`` and
|
|
71
|
+
``target_nodes`` make totality and injectivity checkable without retaining
|
|
72
|
+
mutable graph objects. Public mappings are normalized to sorted tuples so
|
|
73
|
+
instances remain immutable and hashable.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
source: Hashable
|
|
77
|
+
target: Hashable
|
|
78
|
+
source_nodes: frozenset[Hashable]
|
|
79
|
+
target_nodes: frozenset[Hashable]
|
|
80
|
+
f: tuple[tuple[Hashable, Hashable], ...]
|
|
81
|
+
theta: tuple[tuple[Hashable, WildcardConstraint], ...] = ()
|
|
82
|
+
|
|
83
|
+
def __post_init__(self) -> None:
|
|
84
|
+
object.__setattr__(self, "source_nodes", frozenset(self.source_nodes))
|
|
85
|
+
object.__setattr__(self, "target_nodes", frozenset(self.target_nodes))
|
|
86
|
+
pairs = _pairs(self.f)
|
|
87
|
+
substitutions = _pairs(self.theta)
|
|
88
|
+
object.__setattr__(self, "f", _sorted_pairs(pairs))
|
|
89
|
+
object.__setattr__(self, "theta", _sorted_pairs(substitutions))
|
|
90
|
+
|
|
91
|
+
source_keys = tuple(key for key, _ in pairs)
|
|
92
|
+
target_values = tuple(value for _, value in pairs)
|
|
93
|
+
issues: list[MorphismIssue] = []
|
|
94
|
+
if len(set(source_keys)) != len(source_keys):
|
|
95
|
+
issues.append(
|
|
96
|
+
MorphismIssue(
|
|
97
|
+
MorphismIssueCode.DUPLICATE_SOURCE,
|
|
98
|
+
"A source node is mapped more than once.",
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
if len(set(target_values)) != len(target_values):
|
|
102
|
+
issues.append(
|
|
103
|
+
MorphismIssue(
|
|
104
|
+
MorphismIssueCode.NON_INJECTIVE,
|
|
105
|
+
"Material source nodes must have distinct target images.",
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
outside_source = set(source_keys) - set(self.source_nodes)
|
|
109
|
+
if outside_source:
|
|
110
|
+
issues.append(
|
|
111
|
+
MorphismIssue(
|
|
112
|
+
MorphismIssueCode.OUTSIDE_SOURCE,
|
|
113
|
+
"The embedding contains nodes outside its source.",
|
|
114
|
+
{"nodes": tuple(sorted(map(repr, outside_source)))},
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
outside_target = set(target_values) - set(self.target_nodes)
|
|
118
|
+
if outside_target:
|
|
119
|
+
issues.append(
|
|
120
|
+
MorphismIssue(
|
|
121
|
+
MorphismIssueCode.OUTSIDE_TARGET,
|
|
122
|
+
"The embedding contains images outside its target.",
|
|
123
|
+
{"nodes": tuple(sorted(map(repr, outside_target)))},
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
missing = set(self.source_nodes) - set(source_keys)
|
|
127
|
+
if missing:
|
|
128
|
+
issues.append(
|
|
129
|
+
MorphismIssue(
|
|
130
|
+
MorphismIssueCode.PARTIAL_MAPPING,
|
|
131
|
+
"A graph morphism must map every material source node.",
|
|
132
|
+
{"nodes": tuple(sorted(map(repr, missing)))},
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
theta_keys = tuple(key for key, _ in substitutions)
|
|
136
|
+
if len(set(theta_keys)) != len(theta_keys):
|
|
137
|
+
issues.append(
|
|
138
|
+
MorphismIssue(
|
|
139
|
+
MorphismIssueCode.DUPLICATE_SOURCE,
|
|
140
|
+
"A source wildcard has more than one substitution.",
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
outside_theta = set(theta_keys) - set(self.source_nodes)
|
|
144
|
+
if outside_theta:
|
|
145
|
+
issues.append(
|
|
146
|
+
MorphismIssue(
|
|
147
|
+
MorphismIssueCode.OUTSIDE_SOURCE,
|
|
148
|
+
"The substitution environment contains unknown source nodes.",
|
|
149
|
+
{"nodes": tuple(sorted(map(repr, outside_theta)))},
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
outside_owners = {
|
|
153
|
+
constraint.owner
|
|
154
|
+
for _, constraint in substitutions
|
|
155
|
+
if constraint.owner is not None
|
|
156
|
+
and constraint.owner not in self.source_nodes
|
|
157
|
+
}
|
|
158
|
+
if outside_owners:
|
|
159
|
+
issues.append(
|
|
160
|
+
MorphismIssue(
|
|
161
|
+
MorphismIssueCode.OWNER_OUTSIDE_SOURCE,
|
|
162
|
+
"Wildcard owners must be material nodes in the morphism source.",
|
|
163
|
+
{"nodes": tuple(sorted(map(repr, outside_owners)))},
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
if any(not isinstance(value, WildcardConstraint) for _, value in substitutions):
|
|
167
|
+
raise TypeError("theta values must be WildcardConstraint instances.")
|
|
168
|
+
if issues:
|
|
169
|
+
raise GraphMorphismError(*issues)
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def mapping(self) -> dict[Hashable, Hashable]:
|
|
173
|
+
"""Return a disposable mapping view of ``f``."""
|
|
174
|
+
return dict(self.f)
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def substitutions(self) -> dict[Hashable, WildcardConstraint]:
|
|
178
|
+
"""Return a disposable mapping view of ``theta``."""
|
|
179
|
+
return dict(self.theta)
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def identity(
|
|
183
|
+
cls,
|
|
184
|
+
object_id: Hashable,
|
|
185
|
+
nodes: frozenset[Hashable] | set[Hashable] | tuple[Hashable, ...],
|
|
186
|
+
theta: Mapping[Hashable, WildcardConstraint] | None = None,
|
|
187
|
+
) -> "GraphMorphism":
|
|
188
|
+
"""Return the identity morphism for one endpoint object."""
|
|
189
|
+
frozen_nodes = frozenset(nodes)
|
|
190
|
+
return cls(
|
|
191
|
+
object_id,
|
|
192
|
+
object_id,
|
|
193
|
+
frozen_nodes,
|
|
194
|
+
frozen_nodes,
|
|
195
|
+
tuple((node, node) for node in frozen_nodes),
|
|
196
|
+
tuple((theta or {}).items()),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
def then(self, after: "GraphMorphism") -> "GraphMorphism":
|
|
200
|
+
"""Return ``after ∘ self`` with normalized constraint intersection."""
|
|
201
|
+
if self.target != after.source or self.target_nodes != after.source_nodes:
|
|
202
|
+
raise GraphMorphismError(
|
|
203
|
+
MorphismIssue(
|
|
204
|
+
MorphismIssueCode.ENDPOINT_MISMATCH,
|
|
205
|
+
"Composable morphisms must share the same intermediate object.",
|
|
206
|
+
{
|
|
207
|
+
"left_target": repr(self.target),
|
|
208
|
+
"right_source": repr(after.source),
|
|
209
|
+
},
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
left_map = self.mapping
|
|
214
|
+
right_map = after.mapping
|
|
215
|
+
composed: dict[Hashable, Hashable] = {}
|
|
216
|
+
for source_node, intermediate in left_map.items():
|
|
217
|
+
if intermediate not in right_map:
|
|
218
|
+
raise GraphMorphismError(
|
|
219
|
+
MorphismIssue(
|
|
220
|
+
MorphismIssueCode.MISSING_INTERMEDIATE,
|
|
221
|
+
"The second morphism does not map an intermediate image.",
|
|
222
|
+
{"node": repr(intermediate)},
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
composed[source_node] = right_map[intermediate]
|
|
226
|
+
|
|
227
|
+
left_theta = self.substitutions
|
|
228
|
+
right_theta = after.substitutions
|
|
229
|
+
inverse_left = {target: source for source, target in self.f}
|
|
230
|
+
composed_theta: dict[Hashable, WildcardConstraint] = {}
|
|
231
|
+
for source_node, intermediate in left_map.items():
|
|
232
|
+
left_constraint = left_theta.get(source_node)
|
|
233
|
+
right_constraint = right_theta.get(intermediate)
|
|
234
|
+
if right_constraint is not None:
|
|
235
|
+
if (
|
|
236
|
+
right_constraint.owner is not None
|
|
237
|
+
and right_constraint.owner not in inverse_left
|
|
238
|
+
):
|
|
239
|
+
raise GraphMorphismError(
|
|
240
|
+
MorphismIssue(
|
|
241
|
+
MorphismIssueCode.OWNER_OUTSIDE_IMAGE,
|
|
242
|
+
"A composed wildcard owner lies outside the first image.",
|
|
243
|
+
{"owner": repr(right_constraint.owner)},
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
right_constraint = right_constraint.relabel_owner(inverse_left)
|
|
247
|
+
if left_constraint is None and right_constraint is None:
|
|
248
|
+
continue
|
|
249
|
+
if left_constraint is None:
|
|
250
|
+
composed_theta[source_node] = right_constraint # type: ignore[assignment]
|
|
251
|
+
continue
|
|
252
|
+
if right_constraint is None:
|
|
253
|
+
composed_theta[source_node] = left_constraint
|
|
254
|
+
continue
|
|
255
|
+
result = left_constraint.intersect(right_constraint)
|
|
256
|
+
if not result.valid:
|
|
257
|
+
raise GraphMorphismError(
|
|
258
|
+
MorphismIssue(
|
|
259
|
+
MorphismIssueCode.CONSTRAINT_CONTRADICTION,
|
|
260
|
+
"Wildcard constraints contradict during composition.",
|
|
261
|
+
{"source_node": repr(source_node)},
|
|
262
|
+
result.issues,
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
composed_theta[source_node] = result.constraint # type: ignore[assignment]
|
|
266
|
+
|
|
267
|
+
return GraphMorphism(
|
|
268
|
+
self.source,
|
|
269
|
+
after.target,
|
|
270
|
+
self.source_nodes,
|
|
271
|
+
after.target_nodes,
|
|
272
|
+
tuple(composed.items()),
|
|
273
|
+
tuple(composed_theta.items()),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def compose(self, after: "GraphMorphism") -> "GraphMorphism":
|
|
277
|
+
"""Alias for :meth:`then`; computes ``after ∘ self``."""
|
|
278
|
+
return self.then(after)
|
|
279
|
+
|
|
280
|
+
def relabel(
|
|
281
|
+
self,
|
|
282
|
+
source_labels: Mapping[Hashable, Hashable],
|
|
283
|
+
target_labels: Mapping[Hashable, Hashable],
|
|
284
|
+
*,
|
|
285
|
+
source: Hashable | None = None,
|
|
286
|
+
target: Hashable | None = None,
|
|
287
|
+
) -> "GraphMorphism":
|
|
288
|
+
"""Return an isomorphic morphism under endpoint node bijections."""
|
|
289
|
+
if set(source_labels) != set(self.source_nodes):
|
|
290
|
+
raise ValueError("source_labels must be total on source_nodes.")
|
|
291
|
+
if set(target_labels) != set(self.target_nodes):
|
|
292
|
+
raise ValueError("target_labels must be total on target_nodes.")
|
|
293
|
+
return GraphMorphism(
|
|
294
|
+
self.source if source is None else source,
|
|
295
|
+
self.target if target is None else target,
|
|
296
|
+
frozenset(source_labels.values()),
|
|
297
|
+
frozenset(target_labels.values()),
|
|
298
|
+
tuple(
|
|
299
|
+
(source_labels[left], target_labels[right]) for left, right in self.f
|
|
300
|
+
),
|
|
301
|
+
tuple(
|
|
302
|
+
(source_labels[node], constraint.relabel_owner(source_labels))
|
|
303
|
+
for node, constraint in self.theta
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def canonical_signature(self) -> tuple[Any, ...]:
|
|
308
|
+
"""Return a node-numbering-independent contract signature.
|
|
309
|
+
|
|
310
|
+
Sprint 14 morphisms intentionally retain no graph adjacency. Their
|
|
311
|
+
numbering-independent content is therefore embedding cardinality plus
|
|
312
|
+
canonical bundles of normalized constraints and owner incidence.
|
|
313
|
+
Sprint 15 can extend this with graph incidence when it constructs
|
|
314
|
+
fusion diagrams.
|
|
315
|
+
"""
|
|
316
|
+
base_by_node: dict[Hashable, tuple[Any, ...] | None] = {
|
|
317
|
+
node: None for node in self.source_nodes
|
|
318
|
+
}
|
|
319
|
+
incoming: dict[Hashable, list[tuple[Any, ...]]] = {
|
|
320
|
+
node: [] for node in self.source_nodes
|
|
321
|
+
}
|
|
322
|
+
for node, constraint in self.theta:
|
|
323
|
+
values = list(constraint.normalized())
|
|
324
|
+
values[6] = None
|
|
325
|
+
base = tuple(values)
|
|
326
|
+
base_by_node[node] = base
|
|
327
|
+
if constraint.owner is not None:
|
|
328
|
+
incoming[constraint.owner].append(base)
|
|
329
|
+
owner_bundles = tuple(
|
|
330
|
+
sorted(
|
|
331
|
+
[
|
|
332
|
+
(
|
|
333
|
+
base_by_node[node],
|
|
334
|
+
tuple(sorted(incoming[node], key=repr)),
|
|
335
|
+
)
|
|
336
|
+
for node in self.source_nodes
|
|
337
|
+
],
|
|
338
|
+
key=repr,
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
return (len(self.source_nodes), len(self.target_nodes), owner_bundles)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
__all__ = [
|
|
345
|
+
"GraphMorphism",
|
|
346
|
+
"GraphMorphismError",
|
|
347
|
+
"MorphismIssue",
|
|
348
|
+
"MorphismIssueCode",
|
|
349
|
+
]
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Stereo-reference-frame transport through graph morphisms."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
|
|
9
|
+
from synkit.Graph.Stereo.descriptors import (
|
|
10
|
+
Reference,
|
|
11
|
+
StereoValue,
|
|
12
|
+
descriptor_id,
|
|
13
|
+
parse_virtual_reference,
|
|
14
|
+
virtual_reference,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StereoEffect(str, Enum):
|
|
19
|
+
"""Declared orientation effect carried by a reaction rule."""
|
|
20
|
+
|
|
21
|
+
RETAIN = "retain"
|
|
22
|
+
INVERT = "invert"
|
|
23
|
+
UNSPECIFIED = "unspecified"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class StereoTransportIssueCode(str, Enum):
|
|
27
|
+
"""Stable stereo-frame transport failure codes."""
|
|
28
|
+
|
|
29
|
+
DELTA_ARITY = "STEREO_DELTA_ARITY"
|
|
30
|
+
DUPLICATE_REFERENCE = "STEREO_DUPLICATE_REFERENCE"
|
|
31
|
+
REFERENCE_NOT_FOUND = "STEREO_REFERENCE_NOT_FOUND"
|
|
32
|
+
OWNER_REPLACEMENT = "STEREO_OWNER_REPLACEMENT"
|
|
33
|
+
MISSING_NODE_MAPPING = "STEREO_MISSING_NODE_MAPPING"
|
|
34
|
+
WRONG_OWNER = "STEREO_WRONG_OWNER"
|
|
35
|
+
NON_INJECTIVE = "STEREO_NON_INJECTIVE"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class StereoTransportIssue:
|
|
40
|
+
code: StereoTransportIssueCode
|
|
41
|
+
message: str
|
|
42
|
+
context: Mapping[str, Any] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> dict[str, Any]:
|
|
45
|
+
return {
|
|
46
|
+
"code": self.code.value,
|
|
47
|
+
"message": self.message,
|
|
48
|
+
"context": dict(self.context),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class StereoTransportError(ValueError):
|
|
53
|
+
"""Raised when an ordered stereo frame cannot be transported safely."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, issue: StereoTransportIssue):
|
|
56
|
+
self.issue = issue
|
|
57
|
+
super().__init__(issue.message)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class StereoReferenceDelta:
|
|
62
|
+
"""Explicit ligand replacements and their declared stereo effect.
|
|
63
|
+
|
|
64
|
+
Removed references belong to the source frame. Added references belong
|
|
65
|
+
to the target frame; they are therefore not passed through the node map.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
removed: tuple[Reference, ...] = ()
|
|
69
|
+
added: tuple[Reference, ...] = ()
|
|
70
|
+
effect: StereoEffect = StereoEffect.RETAIN
|
|
71
|
+
|
|
72
|
+
def __post_init__(self) -> None:
|
|
73
|
+
if not isinstance(self.effect, StereoEffect):
|
|
74
|
+
object.__setattr__(self, "effect", StereoEffect(self.effect))
|
|
75
|
+
if len(self.removed) != len(self.added):
|
|
76
|
+
raise StereoTransportError(
|
|
77
|
+
StereoTransportIssue(
|
|
78
|
+
StereoTransportIssueCode.DELTA_ARITY,
|
|
79
|
+
"Removed and added stereo references must pair one-to-one.",
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
if len(set(self.removed)) != len(self.removed) or len(set(self.added)) != len(
|
|
83
|
+
self.added
|
|
84
|
+
):
|
|
85
|
+
raise StereoTransportError(
|
|
86
|
+
StereoTransportIssue(
|
|
87
|
+
StereoTransportIssueCode.DUPLICATE_REFERENCE,
|
|
88
|
+
"Stereo reference deltas cannot contain duplicate endpoints.",
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def replacements(self) -> dict[Reference, Reference]:
|
|
94
|
+
return dict(zip(self.removed, self.added, strict=True))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _owner_positions(descriptor: StereoValue) -> frozenset[int]:
|
|
98
|
+
if descriptor.descriptor_class in {
|
|
99
|
+
"tetrahedral",
|
|
100
|
+
"square_planar",
|
|
101
|
+
"trigonal_bipyramidal",
|
|
102
|
+
"octahedral",
|
|
103
|
+
}:
|
|
104
|
+
return frozenset({0})
|
|
105
|
+
return frozenset({2, 3})
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _transport_reference(
|
|
109
|
+
reference: Reference,
|
|
110
|
+
mapping: Mapping[int, int],
|
|
111
|
+
) -> Reference:
|
|
112
|
+
if type(reference) is int:
|
|
113
|
+
if reference not in mapping:
|
|
114
|
+
raise StereoTransportError(
|
|
115
|
+
StereoTransportIssue(
|
|
116
|
+
StereoTransportIssueCode.MISSING_NODE_MAPPING,
|
|
117
|
+
"Every retained material stereo reference must be mapped.",
|
|
118
|
+
{"reference": reference},
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
return mapping[reference]
|
|
122
|
+
virtual = parse_virtual_reference(reference)
|
|
123
|
+
if virtual is None:
|
|
124
|
+
raise StereoTransportError(
|
|
125
|
+
StereoTransportIssue(
|
|
126
|
+
StereoTransportIssueCode.WRONG_OWNER,
|
|
127
|
+
"Stereo references must be material nodes or typed virtual references.",
|
|
128
|
+
{"reference": repr(reference)},
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
if virtual.center not in mapping:
|
|
132
|
+
raise StereoTransportError(
|
|
133
|
+
StereoTransportIssue(
|
|
134
|
+
StereoTransportIssueCode.MISSING_NODE_MAPPING,
|
|
135
|
+
"The owner of a retained virtual stereo reference must be mapped.",
|
|
136
|
+
{"owner": virtual.center},
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
return virtual_reference(virtual.kind, mapping[virtual.center])
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def transport_stereo_descriptor(
|
|
143
|
+
descriptor: StereoValue,
|
|
144
|
+
mapping: Mapping[int, int],
|
|
145
|
+
delta: StereoReferenceDelta | None = None,
|
|
146
|
+
) -> StereoValue:
|
|
147
|
+
"""Transport an ordered stereo frame through an injective node mapping."""
|
|
148
|
+
change = delta or StereoReferenceDelta()
|
|
149
|
+
replacements = change.replacements
|
|
150
|
+
atoms = descriptor.atoms
|
|
151
|
+
retained_material = {
|
|
152
|
+
reference
|
|
153
|
+
for reference in atoms
|
|
154
|
+
if type(reference) is int and reference not in replacements
|
|
155
|
+
}
|
|
156
|
+
retained_images = [
|
|
157
|
+
mapping[reference] for reference in retained_material if reference in mapping
|
|
158
|
+
]
|
|
159
|
+
if len(set(retained_images)) != len(retained_images):
|
|
160
|
+
raise StereoTransportError(
|
|
161
|
+
StereoTransportIssue(
|
|
162
|
+
StereoTransportIssueCode.NON_INJECTIVE,
|
|
163
|
+
"Material stereo references must retain distinct graph images.",
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
missing = tuple(reference for reference in change.removed if reference not in atoms)
|
|
167
|
+
if missing:
|
|
168
|
+
raise StereoTransportError(
|
|
169
|
+
StereoTransportIssue(
|
|
170
|
+
StereoTransportIssueCode.REFERENCE_NOT_FOUND,
|
|
171
|
+
"A removed ligand is not present in the source stereo frame.",
|
|
172
|
+
{"references": tuple(map(repr, missing))},
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
owner_positions = _owner_positions(descriptor)
|
|
176
|
+
replaced_owners = tuple(
|
|
177
|
+
atoms[position]
|
|
178
|
+
for position in owner_positions
|
|
179
|
+
if atoms[position] in replacements
|
|
180
|
+
)
|
|
181
|
+
if replaced_owners:
|
|
182
|
+
raise StereoTransportError(
|
|
183
|
+
StereoTransportIssue(
|
|
184
|
+
StereoTransportIssueCode.OWNER_REPLACEMENT,
|
|
185
|
+
"Reference deltas replace ligands, not descriptor owners.",
|
|
186
|
+
{"owners": tuple(map(repr, replaced_owners))},
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
transported = tuple(
|
|
191
|
+
(
|
|
192
|
+
replacements[reference]
|
|
193
|
+
if reference in replacements
|
|
194
|
+
else _transport_reference(reference, mapping)
|
|
195
|
+
)
|
|
196
|
+
for reference in atoms
|
|
197
|
+
)
|
|
198
|
+
try:
|
|
199
|
+
result = type(descriptor)(transported, descriptor.parity, descriptor.provenance)
|
|
200
|
+
except ValueError as exc:
|
|
201
|
+
raise StereoTransportError(
|
|
202
|
+
StereoTransportIssue(
|
|
203
|
+
StereoTransportIssueCode.WRONG_OWNER,
|
|
204
|
+
"The transported frame violates owner or ligand-slot semantics.",
|
|
205
|
+
{"reason": str(exc)},
|
|
206
|
+
)
|
|
207
|
+
) from exc
|
|
208
|
+
|
|
209
|
+
if change.effect is StereoEffect.INVERT:
|
|
210
|
+
return result.invert()
|
|
211
|
+
if change.effect is StereoEffect.UNSPECIFIED:
|
|
212
|
+
return type(result)(result.atoms, None, result.provenance)
|
|
213
|
+
return result
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def transport_stereo_registry(
|
|
217
|
+
registry: Mapping[str, StereoValue],
|
|
218
|
+
mapping: Mapping[int, int],
|
|
219
|
+
deltas: Mapping[str, StereoReferenceDelta] | None = None,
|
|
220
|
+
) -> dict[str, StereoValue]:
|
|
221
|
+
"""Transport a descriptor registry while preserving deterministic keys."""
|
|
222
|
+
changes = deltas or {}
|
|
223
|
+
unknown = set(changes) - set(registry)
|
|
224
|
+
if unknown:
|
|
225
|
+
raise StereoTransportError(
|
|
226
|
+
StereoTransportIssue(
|
|
227
|
+
StereoTransportIssueCode.REFERENCE_NOT_FOUND,
|
|
228
|
+
"A stereo delta names a descriptor absent from the source registry.",
|
|
229
|
+
{"descriptor_ids": tuple(sorted(unknown))},
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
transported: dict[str, StereoValue] = {}
|
|
233
|
+
for source_id in sorted(registry):
|
|
234
|
+
descriptor = transport_stereo_descriptor(
|
|
235
|
+
registry[source_id], mapping, changes.get(source_id)
|
|
236
|
+
)
|
|
237
|
+
target_id = descriptor_id(descriptor)
|
|
238
|
+
if target_id in transported:
|
|
239
|
+
raise StereoTransportError(
|
|
240
|
+
StereoTransportIssue(
|
|
241
|
+
StereoTransportIssueCode.NON_INJECTIVE,
|
|
242
|
+
"Two source descriptors collapse to one target owner.",
|
|
243
|
+
{"descriptor_id": target_id},
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
transported[target_id] = descriptor
|
|
247
|
+
return transported
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
__all__ = [
|
|
251
|
+
"StereoEffect",
|
|
252
|
+
"StereoReferenceDelta",
|
|
253
|
+
"StereoTransportError",
|
|
254
|
+
"StereoTransportIssue",
|
|
255
|
+
"StereoTransportIssueCode",
|
|
256
|
+
"transport_stereo_descriptor",
|
|
257
|
+
"transport_stereo_registry",
|
|
258
|
+
]
|