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,577 @@
|
|
|
1
|
+
"""Finite occurrence processes with explicit material flow and concurrency."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Hashable, Iterable, Mapping
|
|
8
|
+
|
|
9
|
+
import networkx as nx
|
|
10
|
+
|
|
11
|
+
from synkit.Graph.Morphism import LLGError, LLGMorphism, LewisLabelledGraph
|
|
12
|
+
from synkit.Rule.Apply import RuleSpan
|
|
13
|
+
from synkit.Rule.Compose import (
|
|
14
|
+
CommutationCertificate,
|
|
15
|
+
LawIssue,
|
|
16
|
+
rule_spans_isomorphic,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ProcessIssueCode(str, Enum):
|
|
21
|
+
"""Stable failures for occurrence values, flows, orders, and choices."""
|
|
22
|
+
|
|
23
|
+
DUPLICATE_ID = "PROCESS_DUPLICATE_ID"
|
|
24
|
+
UNKNOWN_MATERIAL = "PROCESS_UNKNOWN_MATERIAL"
|
|
25
|
+
UNKNOWN_EVENT = "PROCESS_UNKNOWN_EVENT"
|
|
26
|
+
ENDPOINT_PARTITION = "PROCESS_ENDPOINT_PARTITION"
|
|
27
|
+
BINDING_NOT_ISOMORPHIC = "PROCESS_BINDING_NOT_ISOMORPHIC"
|
|
28
|
+
MATERIAL_REUSED = "PROCESS_MATERIAL_REUSED"
|
|
29
|
+
CAUSAL_CYCLE = "PROCESS_CAUSAL_CYCLE"
|
|
30
|
+
MISSING_INDEPENDENCE = "PROCESS_MISSING_INDEPENDENCE"
|
|
31
|
+
INVALID_INDEPENDENCE = "PROCESS_INVALID_INDEPENDENCE"
|
|
32
|
+
EXTRA_INDEPENDENCE = "PROCESS_EXTRA_INDEPENDENCE"
|
|
33
|
+
INVALID_EXTENSION = "PROCESS_INVALID_LINEAR_EXTENSION"
|
|
34
|
+
EXTENSION_LIMIT = "PROCESS_LINEAR_EXTENSION_LIMIT"
|
|
35
|
+
SERIES_PARALLEL_LIMIT = "PROCESS_SERIES_PARALLEL_LIMIT"
|
|
36
|
+
INVALID_ALTERNATIVE = "PROCESS_INVALID_ALTERNATIVE"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class ProcessIssue:
|
|
41
|
+
code: ProcessIssueCode
|
|
42
|
+
message: str
|
|
43
|
+
context: Mapping[str, Any] = field(default_factory=dict)
|
|
44
|
+
|
|
45
|
+
def to_dict(self) -> dict[str, Any]:
|
|
46
|
+
return {
|
|
47
|
+
"code": self.code.value,
|
|
48
|
+
"message": self.message,
|
|
49
|
+
"context": dict(self.context),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ProcessError(ValueError):
|
|
54
|
+
"""Raised when occurrence-process data do not prove their semantics."""
|
|
55
|
+
|
|
56
|
+
def __init__(self, *issues: ProcessIssue):
|
|
57
|
+
self.issues = tuple(issues)
|
|
58
|
+
super().__init__("; ".join(issue.message for issue in issues))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class MaterialOccurrence:
|
|
63
|
+
"""One material copy; isomorphic values do not identify copy IDs."""
|
|
64
|
+
|
|
65
|
+
occurrence_id: str
|
|
66
|
+
value: LewisLabelledGraph
|
|
67
|
+
|
|
68
|
+
def __post_init__(self) -> None:
|
|
69
|
+
if not self.occurrence_id:
|
|
70
|
+
raise ValueError("A material occurrence requires a non-empty ID.")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True)
|
|
74
|
+
class MaterialBinding:
|
|
75
|
+
"""An endpoint component isomorphism to one material occurrence."""
|
|
76
|
+
|
|
77
|
+
material_id: str
|
|
78
|
+
endpoint_nodes: frozenset[Hashable]
|
|
79
|
+
mapping: tuple[tuple[Hashable, Hashable], ...]
|
|
80
|
+
|
|
81
|
+
def __post_init__(self) -> None:
|
|
82
|
+
object.__setattr__(self, "endpoint_nodes", frozenset(self.endpoint_nodes))
|
|
83
|
+
pairs = (
|
|
84
|
+
self.mapping.items() if isinstance(self.mapping, Mapping) else self.mapping
|
|
85
|
+
)
|
|
86
|
+
object.__setattr__(self, "mapping", tuple(sorted(pairs, key=repr)))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True)
|
|
90
|
+
class RuleOccurrence:
|
|
91
|
+
"""One event identity, independently of equality of its rule value."""
|
|
92
|
+
|
|
93
|
+
occurrence_id: str
|
|
94
|
+
rule: RuleSpan
|
|
95
|
+
inputs: tuple[MaterialBinding, ...] = ()
|
|
96
|
+
outputs: tuple[MaterialBinding, ...] = ()
|
|
97
|
+
|
|
98
|
+
def __post_init__(self) -> None:
|
|
99
|
+
if not self.occurrence_id:
|
|
100
|
+
raise ValueError("A rule occurrence requires a non-empty ID.")
|
|
101
|
+
object.__setattr__(self, "inputs", tuple(self.inputs))
|
|
102
|
+
object.__setattr__(self, "outputs", tuple(self.outputs))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class IndependenceWitness:
|
|
107
|
+
"""One incomparable event pair and its executable commutation proof."""
|
|
108
|
+
|
|
109
|
+
first_event: str
|
|
110
|
+
second_event: str
|
|
111
|
+
certificate: CommutationCertificate
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def key(self) -> frozenset[str]:
|
|
115
|
+
return frozenset((self.first_event, self.second_event))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@dataclass(frozen=True)
|
|
119
|
+
class LinearExtensionEquivalence:
|
|
120
|
+
"""A path of adjacent certified-independent swaps between extensions."""
|
|
121
|
+
|
|
122
|
+
source: tuple[str, ...]
|
|
123
|
+
target: tuple[str, ...]
|
|
124
|
+
swaps: tuple[tuple[str, str], ...]
|
|
125
|
+
|
|
126
|
+
def replay(self, process: "OccurrenceProcess") -> bool:
|
|
127
|
+
if not process.is_linear_extension(
|
|
128
|
+
self.source
|
|
129
|
+
) or not process.is_linear_extension(self.target):
|
|
130
|
+
return False
|
|
131
|
+
current = list(self.source)
|
|
132
|
+
witnesses = process.independence_by_pair
|
|
133
|
+
for left, right in self.swaps:
|
|
134
|
+
try:
|
|
135
|
+
index = next(
|
|
136
|
+
position
|
|
137
|
+
for position in range(len(current) - 1)
|
|
138
|
+
if current[position : position + 2] == [left, right]
|
|
139
|
+
)
|
|
140
|
+
except StopIteration:
|
|
141
|
+
return False
|
|
142
|
+
witness = witnesses.get(frozenset((left, right)))
|
|
143
|
+
if witness is None or not witness.certificate.replay():
|
|
144
|
+
return False
|
|
145
|
+
current[index], current[index + 1] = right, left
|
|
146
|
+
return tuple(current) == self.target
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _component_graph(
|
|
150
|
+
endpoint: LewisLabelledGraph, nodes: frozenset[Hashable]
|
|
151
|
+
) -> LewisLabelledGraph:
|
|
152
|
+
graph = endpoint.to_networkx().subgraph(nodes).copy()
|
|
153
|
+
return LewisLabelledGraph.from_networkx(
|
|
154
|
+
graph, endpoint.schema, name=(endpoint.name, "component")
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _components(endpoint: LewisLabelledGraph) -> frozenset[frozenset[Hashable]]:
|
|
159
|
+
return frozenset(
|
|
160
|
+
frozenset(component)
|
|
161
|
+
for component in nx.connected_components(endpoint.to_networkx())
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _witness_rule_matches(declared: RuleSpan, witnessed: RuleSpan) -> bool:
|
|
166
|
+
"""Require the supplied execution contract as well as the carrier span.
|
|
167
|
+
|
|
168
|
+
Arbitrary guard equivalence or guard transport is not inferred from span
|
|
169
|
+
isomorphism. A guarded event must use the same condition object that the
|
|
170
|
+
finite commutation witness executed.
|
|
171
|
+
"""
|
|
172
|
+
return (
|
|
173
|
+
declared.condition is witnessed.condition
|
|
174
|
+
and declared.boundary == witnessed.boundary
|
|
175
|
+
and declared.environment == witnessed.environment
|
|
176
|
+
and rule_spans_isomorphic(declared, witnessed)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@dataclass(frozen=True)
|
|
181
|
+
class OccurrenceProcess:
|
|
182
|
+
"""A finite causal configuration with explicit material-copy flow."""
|
|
183
|
+
|
|
184
|
+
process_id: str
|
|
185
|
+
materials: tuple[MaterialOccurrence, ...]
|
|
186
|
+
events: tuple[RuleOccurrence, ...]
|
|
187
|
+
independence: tuple[IndependenceWitness, ...] = ()
|
|
188
|
+
|
|
189
|
+
def __post_init__(self) -> None:
|
|
190
|
+
object.__setattr__(self, "materials", tuple(self.materials))
|
|
191
|
+
object.__setattr__(self, "events", tuple(self.events))
|
|
192
|
+
object.__setattr__(self, "independence", tuple(self.independence))
|
|
193
|
+
issues = self._validation_issues()
|
|
194
|
+
if issues:
|
|
195
|
+
raise ProcessError(*issues)
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def material_by_id(self) -> dict[str, MaterialOccurrence]:
|
|
199
|
+
return {item.occurrence_id: item for item in self.materials}
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def event_by_id(self) -> dict[str, RuleOccurrence]:
|
|
203
|
+
return {item.occurrence_id: item for item in self.events}
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def independence_by_pair(self) -> dict[frozenset[str], IndependenceWitness]:
|
|
207
|
+
return {item.key: item for item in self.independence}
|
|
208
|
+
|
|
209
|
+
def _binding_issues(
|
|
210
|
+
self,
|
|
211
|
+
event: RuleOccurrence,
|
|
212
|
+
bindings: tuple[MaterialBinding, ...],
|
|
213
|
+
endpoint: LewisLabelledGraph,
|
|
214
|
+
side: str,
|
|
215
|
+
) -> list[ProcessIssue]:
|
|
216
|
+
issues: list[ProcessIssue] = []
|
|
217
|
+
materials = self.material_by_id
|
|
218
|
+
expected = _components(endpoint)
|
|
219
|
+
observed = frozenset(binding.endpoint_nodes for binding in bindings)
|
|
220
|
+
if observed != expected or len(observed) != len(bindings):
|
|
221
|
+
issues.append(
|
|
222
|
+
ProcessIssue(
|
|
223
|
+
ProcessIssueCode.ENDPOINT_PARTITION,
|
|
224
|
+
"Bindings must partition an endpoint into connected components.",
|
|
225
|
+
{"event": event.occurrence_id, "side": side},
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
material_ids = tuple(binding.material_id for binding in bindings)
|
|
229
|
+
if len(set(material_ids)) != len(material_ids):
|
|
230
|
+
issues.append(
|
|
231
|
+
ProcessIssue(
|
|
232
|
+
ProcessIssueCode.MATERIAL_REUSED,
|
|
233
|
+
"One material copy cannot fill two endpoint components.",
|
|
234
|
+
{"event": event.occurrence_id, "side": side},
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
for binding in bindings:
|
|
238
|
+
material = materials.get(binding.material_id)
|
|
239
|
+
if material is None:
|
|
240
|
+
issues.append(
|
|
241
|
+
ProcessIssue(
|
|
242
|
+
ProcessIssueCode.UNKNOWN_MATERIAL,
|
|
243
|
+
"A binding names an unknown material occurrence.",
|
|
244
|
+
{"event": event.occurrence_id, "material": binding.material_id},
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
continue
|
|
248
|
+
if binding.endpoint_nodes not in expected:
|
|
249
|
+
continue
|
|
250
|
+
component = _component_graph(endpoint, binding.endpoint_nodes)
|
|
251
|
+
try:
|
|
252
|
+
morphism = LLGMorphism(component, material.value, binding.mapping)
|
|
253
|
+
except LLGError as error:
|
|
254
|
+
issues.append(
|
|
255
|
+
ProcessIssue(
|
|
256
|
+
ProcessIssueCode.BINDING_NOT_ISOMORPHIC,
|
|
257
|
+
"A material binding is not a strict component isomorphism.",
|
|
258
|
+
{
|
|
259
|
+
"event": event.occurrence_id,
|
|
260
|
+
"material": binding.material_id,
|
|
261
|
+
"issues": tuple(issue.to_dict() for issue in error.issues),
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
if not morphism.is_isomorphism:
|
|
267
|
+
issues.append(
|
|
268
|
+
ProcessIssue(
|
|
269
|
+
ProcessIssueCode.BINDING_NOT_ISOMORPHIC,
|
|
270
|
+
"A binding must cover the whole material occurrence.",
|
|
271
|
+
{
|
|
272
|
+
"event": event.occurrence_id,
|
|
273
|
+
"material": binding.material_id,
|
|
274
|
+
},
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
return issues
|
|
278
|
+
|
|
279
|
+
def _flow_graph(self) -> tuple[nx.DiGraph, list[ProcessIssue]]:
|
|
280
|
+
graph = nx.DiGraph()
|
|
281
|
+
graph.add_nodes_from(event.occurrence_id for event in self.events)
|
|
282
|
+
producers: dict[str, str] = {}
|
|
283
|
+
consumers: dict[str, str] = {}
|
|
284
|
+
issues: list[ProcessIssue] = []
|
|
285
|
+
for event in self.events:
|
|
286
|
+
for binding in event.outputs:
|
|
287
|
+
if binding.material_id in producers:
|
|
288
|
+
issues.append(
|
|
289
|
+
ProcessIssue(
|
|
290
|
+
ProcessIssueCode.MATERIAL_REUSED,
|
|
291
|
+
"A material occurrence has more than one producer.",
|
|
292
|
+
{"material": binding.material_id},
|
|
293
|
+
)
|
|
294
|
+
)
|
|
295
|
+
producers[binding.material_id] = event.occurrence_id
|
|
296
|
+
for binding in event.inputs:
|
|
297
|
+
if binding.material_id in consumers:
|
|
298
|
+
issues.append(
|
|
299
|
+
ProcessIssue(
|
|
300
|
+
ProcessIssueCode.MATERIAL_REUSED,
|
|
301
|
+
"A material occurrence has more than one consumer.",
|
|
302
|
+
{"material": binding.material_id},
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
consumers[binding.material_id] = event.occurrence_id
|
|
306
|
+
for material in set(producers) & set(consumers):
|
|
307
|
+
graph.add_edge(producers[material], consumers[material], material=material)
|
|
308
|
+
if not nx.is_directed_acyclic_graph(graph):
|
|
309
|
+
issues.append(
|
|
310
|
+
ProcessIssue(
|
|
311
|
+
ProcessIssueCode.CAUSAL_CYCLE,
|
|
312
|
+
"Occurrence causality must be a finite DAG; unfold repeated cycles.",
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
return graph, issues
|
|
316
|
+
|
|
317
|
+
def _independence_issues(self, graph: nx.DiGraph) -> list[ProcessIssue]:
|
|
318
|
+
if not nx.is_directed_acyclic_graph(graph):
|
|
319
|
+
return []
|
|
320
|
+
closure = nx.transitive_closure_dag(graph)
|
|
321
|
+
event_ids = set(graph)
|
|
322
|
+
incomparable = {
|
|
323
|
+
frozenset((left, right))
|
|
324
|
+
for left in event_ids
|
|
325
|
+
for right in event_ids
|
|
326
|
+
if repr(left) < repr(right)
|
|
327
|
+
and not closure.has_edge(left, right)
|
|
328
|
+
and not closure.has_edge(right, left)
|
|
329
|
+
}
|
|
330
|
+
witnesses = self.independence_by_pair
|
|
331
|
+
issues: list[ProcessIssue] = []
|
|
332
|
+
missing = incomparable - set(witnesses)
|
|
333
|
+
extra = set(witnesses) - incomparable
|
|
334
|
+
if missing:
|
|
335
|
+
issues.append(
|
|
336
|
+
ProcessIssue(
|
|
337
|
+
ProcessIssueCode.MISSING_INDEPENDENCE,
|
|
338
|
+
"Every incomparable event pair needs a commutation certificate.",
|
|
339
|
+
{"pairs": tuple(sorted(map(repr, missing)))},
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
if extra:
|
|
343
|
+
issues.append(
|
|
344
|
+
ProcessIssue(
|
|
345
|
+
ProcessIssueCode.EXTRA_INDEPENDENCE,
|
|
346
|
+
"A causal pair cannot also be declared independent.",
|
|
347
|
+
{"pairs": tuple(sorted(map(repr, extra)))},
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
events = self.event_by_id
|
|
351
|
+
for pair, witness in witnesses.items():
|
|
352
|
+
if len(pair) != 2 or not pair <= event_ids:
|
|
353
|
+
issues.append(
|
|
354
|
+
ProcessIssue(
|
|
355
|
+
ProcessIssueCode.UNKNOWN_EVENT,
|
|
356
|
+
"An independence witness names unknown or identical events.",
|
|
357
|
+
{"pair": repr(pair)},
|
|
358
|
+
)
|
|
359
|
+
)
|
|
360
|
+
continue
|
|
361
|
+
first = events[witness.first_event]
|
|
362
|
+
second = events[witness.second_event]
|
|
363
|
+
certificate = witness.certificate
|
|
364
|
+
first_rule = certificate.first_then_second[0].certificate.rule
|
|
365
|
+
second_rule = certificate.second_then_first[0].certificate.rule
|
|
366
|
+
if (
|
|
367
|
+
not certificate.replay()
|
|
368
|
+
or not _witness_rule_matches(first.rule, first_rule)
|
|
369
|
+
or not _witness_rule_matches(second.rule, second_rule)
|
|
370
|
+
):
|
|
371
|
+
issues.append(
|
|
372
|
+
ProcessIssue(
|
|
373
|
+
ProcessIssueCode.INVALID_INDEPENDENCE,
|
|
374
|
+
"The independence certificate does not prove its event pair.",
|
|
375
|
+
{"pair": tuple(sorted(pair))},
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
return issues
|
|
379
|
+
|
|
380
|
+
def _validation_issues(self) -> list[ProcessIssue]:
|
|
381
|
+
issues: list[ProcessIssue] = []
|
|
382
|
+
material_ids = [item.occurrence_id for item in self.materials]
|
|
383
|
+
event_ids = [item.occurrence_id for item in self.events]
|
|
384
|
+
if len(set(material_ids)) != len(material_ids) or len(set(event_ids)) != len(
|
|
385
|
+
event_ids
|
|
386
|
+
):
|
|
387
|
+
issues.append(
|
|
388
|
+
ProcessIssue(
|
|
389
|
+
ProcessIssueCode.DUPLICATE_ID,
|
|
390
|
+
"Material and event IDs must be unique within their namespaces.",
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
return issues
|
|
394
|
+
for event in self.events:
|
|
395
|
+
issues.extend(
|
|
396
|
+
self._binding_issues(event, event.inputs, event.rule.left, "input")
|
|
397
|
+
)
|
|
398
|
+
issues.extend(
|
|
399
|
+
self._binding_issues(event, event.outputs, event.rule.right, "output")
|
|
400
|
+
)
|
|
401
|
+
graph, flow_issues = self._flow_graph()
|
|
402
|
+
issues.extend(flow_issues)
|
|
403
|
+
issues.extend(self._independence_issues(graph))
|
|
404
|
+
return issues
|
|
405
|
+
|
|
406
|
+
@property
|
|
407
|
+
def causal_graph(self) -> nx.DiGraph:
|
|
408
|
+
graph, issues = self._flow_graph()
|
|
409
|
+
if issues:
|
|
410
|
+
raise ProcessError(*issues)
|
|
411
|
+
return graph
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def causal_pairs(self) -> tuple[tuple[str, str], ...]:
|
|
415
|
+
closure = nx.transitive_closure_dag(self.causal_graph)
|
|
416
|
+
return tuple(sorted(closure.edges, key=repr))
|
|
417
|
+
|
|
418
|
+
@property
|
|
419
|
+
def cover_pairs(self) -> tuple[tuple[str, str], ...]:
|
|
420
|
+
reduction = nx.transitive_reduction(self.causal_graph)
|
|
421
|
+
return tuple(sorted(reduction.edges, key=repr))
|
|
422
|
+
|
|
423
|
+
@property
|
|
424
|
+
def incomparable_pairs(self) -> tuple[frozenset[str], ...]:
|
|
425
|
+
causal = set(self.causal_pairs)
|
|
426
|
+
result = []
|
|
427
|
+
event_ids = sorted(self.event_by_id, key=repr)
|
|
428
|
+
for index, left in enumerate(event_ids):
|
|
429
|
+
for right in event_ids[index + 1 :]:
|
|
430
|
+
if (left, right) not in causal and (right, left) not in causal:
|
|
431
|
+
result.append(frozenset((left, right)))
|
|
432
|
+
return tuple(result)
|
|
433
|
+
|
|
434
|
+
def linear_extensions(
|
|
435
|
+
self, *, max_extensions: int = 10_000
|
|
436
|
+
) -> tuple[tuple[str, ...], ...]:
|
|
437
|
+
"""Enumerate all extensions or raise instead of returning a prefix."""
|
|
438
|
+
if max_extensions <= 0:
|
|
439
|
+
raise ValueError("max_extensions must be positive.")
|
|
440
|
+
result = []
|
|
441
|
+
for extension in nx.all_topological_sorts(self.causal_graph):
|
|
442
|
+
if len(result) >= max_extensions:
|
|
443
|
+
raise ProcessError(
|
|
444
|
+
ProcessIssue(
|
|
445
|
+
ProcessIssueCode.EXTENSION_LIMIT,
|
|
446
|
+
"Linear-extension enumeration exceeded its explicit bound.",
|
|
447
|
+
{"limit": max_extensions},
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
result.append(tuple(extension))
|
|
451
|
+
return tuple(sorted(result, key=repr))
|
|
452
|
+
|
|
453
|
+
def is_linear_extension(self, extension: Iterable[str]) -> bool:
|
|
454
|
+
order = tuple(extension)
|
|
455
|
+
if set(order) != set(self.event_by_id) or len(order) != len(self.events):
|
|
456
|
+
return False
|
|
457
|
+
position = {event: index for index, event in enumerate(order)}
|
|
458
|
+
return all(
|
|
459
|
+
position[left] < position[right] for left, right in self.causal_pairs
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
def extension_equivalence(
|
|
463
|
+
self, source: Iterable[str], target: Iterable[str]
|
|
464
|
+
) -> LinearExtensionEquivalence:
|
|
465
|
+
"""Construct adjacent-independent swaps between two valid extensions."""
|
|
466
|
+
source_order = tuple(source)
|
|
467
|
+
target_order = tuple(target)
|
|
468
|
+
if not self.is_linear_extension(source_order) or not self.is_linear_extension(
|
|
469
|
+
target_order
|
|
470
|
+
):
|
|
471
|
+
raise ProcessError(
|
|
472
|
+
ProcessIssue(
|
|
473
|
+
ProcessIssueCode.INVALID_EXTENSION,
|
|
474
|
+
"Both orders must be linear extensions of this process.",
|
|
475
|
+
)
|
|
476
|
+
)
|
|
477
|
+
current = list(source_order)
|
|
478
|
+
swaps = []
|
|
479
|
+
for target_index, desired in enumerate(target_order):
|
|
480
|
+
position = current.index(desired)
|
|
481
|
+
while position > target_index:
|
|
482
|
+
left, right = current[position - 1], current[position]
|
|
483
|
+
if frozenset((left, right)) not in self.independence_by_pair:
|
|
484
|
+
raise ProcessError(
|
|
485
|
+
ProcessIssue(
|
|
486
|
+
ProcessIssueCode.INVALID_EXTENSION,
|
|
487
|
+
"A required adjacent swap lacks independence evidence.",
|
|
488
|
+
{"pair": (left, right)},
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
current[position - 1], current[position] = right, left
|
|
492
|
+
swaps.append((left, right))
|
|
493
|
+
position -= 1
|
|
494
|
+
witness = LinearExtensionEquivalence(source_order, target_order, tuple(swaps))
|
|
495
|
+
if not witness.replay(self):
|
|
496
|
+
raise ProcessError(
|
|
497
|
+
ProcessIssue(
|
|
498
|
+
ProcessIssueCode.INVALID_EXTENSION,
|
|
499
|
+
"The adjacent-swap equivalence does not replay.",
|
|
500
|
+
)
|
|
501
|
+
)
|
|
502
|
+
return witness
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
@dataclass(frozen=True)
|
|
506
|
+
class ProcessAlternative:
|
|
507
|
+
alternative_id: str
|
|
508
|
+
process: OccurrenceProcess
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@dataclass(frozen=True)
|
|
512
|
+
class ChoiceWitness:
|
|
513
|
+
"""Why two processes remain alternatives rather than one quotient class."""
|
|
514
|
+
|
|
515
|
+
first_alternative: str
|
|
516
|
+
second_alternative: str
|
|
517
|
+
kind: str
|
|
518
|
+
reason: str
|
|
519
|
+
conflicts: tuple[LawIssue, ...] = ()
|
|
520
|
+
|
|
521
|
+
def __post_init__(self) -> None:
|
|
522
|
+
if self.kind not in {"conflict", "material_assignment"}:
|
|
523
|
+
raise ValueError("Choice kind must be conflict or material_assignment.")
|
|
524
|
+
if self.kind == "conflict" and not self.conflicts:
|
|
525
|
+
raise ValueError("A conflict choice requires failed law premises.")
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
@dataclass(frozen=True)
|
|
529
|
+
class OccurrenceProcessFamily:
|
|
530
|
+
"""A retained family of conflicting or materially ambiguous processes."""
|
|
531
|
+
|
|
532
|
+
alternatives: tuple[ProcessAlternative, ...]
|
|
533
|
+
choices: tuple[ChoiceWitness, ...]
|
|
534
|
+
|
|
535
|
+
def __post_init__(self) -> None:
|
|
536
|
+
alternatives = tuple(self.alternatives)
|
|
537
|
+
choices = tuple(self.choices)
|
|
538
|
+
object.__setattr__(self, "alternatives", alternatives)
|
|
539
|
+
object.__setattr__(self, "choices", choices)
|
|
540
|
+
identifiers = tuple(item.alternative_id for item in alternatives)
|
|
541
|
+
issues = []
|
|
542
|
+
if len(set(identifiers)) != len(identifiers):
|
|
543
|
+
issues.append(
|
|
544
|
+
ProcessIssue(
|
|
545
|
+
ProcessIssueCode.DUPLICATE_ID,
|
|
546
|
+
"Process alternative IDs must be unique.",
|
|
547
|
+
)
|
|
548
|
+
)
|
|
549
|
+
known = set(identifiers)
|
|
550
|
+
for choice in choices:
|
|
551
|
+
pair = {choice.first_alternative, choice.second_alternative}
|
|
552
|
+
if len(pair) != 2 or not pair <= known:
|
|
553
|
+
issues.append(
|
|
554
|
+
ProcessIssue(
|
|
555
|
+
ProcessIssueCode.INVALID_ALTERNATIVE,
|
|
556
|
+
"A choice must connect two known distinct alternatives.",
|
|
557
|
+
{"pair": tuple(sorted(pair))},
|
|
558
|
+
)
|
|
559
|
+
)
|
|
560
|
+
if issues:
|
|
561
|
+
raise ProcessError(*issues)
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
__all__ = [
|
|
565
|
+
"ChoiceWitness",
|
|
566
|
+
"IndependenceWitness",
|
|
567
|
+
"LinearExtensionEquivalence",
|
|
568
|
+
"MaterialBinding",
|
|
569
|
+
"MaterialOccurrence",
|
|
570
|
+
"OccurrenceProcess",
|
|
571
|
+
"OccurrenceProcessFamily",
|
|
572
|
+
"ProcessAlternative",
|
|
573
|
+
"ProcessError",
|
|
574
|
+
"ProcessIssue",
|
|
575
|
+
"ProcessIssueCode",
|
|
576
|
+
"RuleOccurrence",
|
|
577
|
+
]
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Structural coexistence queries on explicitly bound occurrence histories."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
from .history import (
|
|
8
|
+
ConfigurationCut,
|
|
9
|
+
HistoryError,
|
|
10
|
+
HistoryIssue,
|
|
11
|
+
HistoryIssueCode,
|
|
12
|
+
OccurrenceMTG,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def cooccurrence_configuration(
|
|
17
|
+
history: OccurrenceMTG, material_ids: Iterable[str]
|
|
18
|
+
) -> ConfigurationCut | None:
|
|
19
|
+
"""Find the least structural ideal containing every requested material copy.
|
|
20
|
+
|
|
21
|
+
The producer downset is necessary for presence. If it contains a requested
|
|
22
|
+
copy's consumer, coexistence is impossible; otherwise that downset is the
|
|
23
|
+
least witnessing ideal. No schedules or ideals are enumerated. This tests
|
|
24
|
+
structural presence, not arbitrary guards, rates or physical feasibility.
|
|
25
|
+
|
|
26
|
+
:param history: Retained occurrence history, revalidated before querying.
|
|
27
|
+
:param material_ids: Known material occurrence IDs; duplicates are ignored.
|
|
28
|
+
:return: Least witnessing cut, or ``None`` if no structural cut exists.
|
|
29
|
+
:raises HistoryError: If the retained history or a material ID is invalid.
|
|
30
|
+
"""
|
|
31
|
+
replay = history.replay()
|
|
32
|
+
if not replay.valid:
|
|
33
|
+
raise HistoryError(*replay.issues)
|
|
34
|
+
requested = frozenset(material_ids)
|
|
35
|
+
unknown = requested - set(history.material_by_id)
|
|
36
|
+
if unknown:
|
|
37
|
+
raise HistoryError(
|
|
38
|
+
HistoryIssue(
|
|
39
|
+
HistoryIssueCode.FLOW_LOOKUP,
|
|
40
|
+
"A coexistence query must name known material occurrence IDs.",
|
|
41
|
+
{"unknown": tuple(sorted(unknown, key=repr))},
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
producers = {
|
|
45
|
+
binding.material_id: event.occurrence_id
|
|
46
|
+
for event in history.events
|
|
47
|
+
for binding in event.outputs
|
|
48
|
+
}
|
|
49
|
+
consumers = {
|
|
50
|
+
binding.material_id: event.occurrence_id
|
|
51
|
+
for event in history.events
|
|
52
|
+
for binding in event.inputs
|
|
53
|
+
}
|
|
54
|
+
predecessors: dict[str, set[str]] = {}
|
|
55
|
+
for first, second in history.causal_pairs:
|
|
56
|
+
predecessors.setdefault(second, set()).add(first)
|
|
57
|
+
pending = [producers[copy] for copy in requested if copy in producers]
|
|
58
|
+
completed: set[str] = set()
|
|
59
|
+
while pending:
|
|
60
|
+
event = pending.pop()
|
|
61
|
+
if event not in completed:
|
|
62
|
+
completed.add(event)
|
|
63
|
+
pending.extend(predecessors.get(event, ()))
|
|
64
|
+
if any(consumers.get(copy) in completed for copy in requested):
|
|
65
|
+
return None
|
|
66
|
+
return history.configuration_cut(completed)
|