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,390 @@
|
|
|
1
|
+
"""Schedule disjoint resumable native subtrees and merge exact records online."""
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import math
|
|
5
|
+
import multiprocessing as mp
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
from collections import deque
|
|
9
|
+
from concurrent.futures import FIRST_COMPLETED, ProcessPoolExecutor, wait
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .candidates import (
|
|
13
|
+
NativeEnumerationStop,
|
|
14
|
+
enumerate_native_candidates,
|
|
15
|
+
prepare_native_candidates,
|
|
16
|
+
)
|
|
17
|
+
from .canonical import compact_code_identifier, transport_canonical_key
|
|
18
|
+
from .orbit_aggregation import OrbitAccumulator
|
|
19
|
+
from .runtime import limit_worker_address_space, pin_worker_cpu, worker_cpus
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import resource
|
|
23
|
+
except ImportError: # Windows has no Unix resource module.
|
|
24
|
+
resource = None
|
|
25
|
+
|
|
26
|
+
_STATE = None
|
|
27
|
+
_ADDRESS_SPACE_LIMIT = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _init(
|
|
31
|
+
lgp,
|
|
32
|
+
target,
|
|
33
|
+
config,
|
|
34
|
+
seed,
|
|
35
|
+
library,
|
|
36
|
+
counter,
|
|
37
|
+
deadline,
|
|
38
|
+
barrier,
|
|
39
|
+
cpu_index,
|
|
40
|
+
cpus,
|
|
41
|
+
prepared_data,
|
|
42
|
+
):
|
|
43
|
+
global _STATE, _ADDRESS_SPACE_LIMIT
|
|
44
|
+
from ....reporting.analysis import _BlindShellObserver, _property_vectors
|
|
45
|
+
from ...slap.lap import _adjacency_and_elements
|
|
46
|
+
|
|
47
|
+
_ADDRESS_SPACE_LIMIT = limit_worker_address_space(resource)
|
|
48
|
+
with cpu_index.get_lock():
|
|
49
|
+
index = cpu_index.value
|
|
50
|
+
cpu_index.value += 1
|
|
51
|
+
pin_worker_cpu(cpus[index % len(cpus)] if cpus else None)
|
|
52
|
+
a, labels = _adjacency_and_elements(lgp[0], False)
|
|
53
|
+
b, _ = _adjacency_and_elements(lgp[1], False)
|
|
54
|
+
observer = _BlindShellObserver(
|
|
55
|
+
a, b, labels, _property_vectors(lgp, config.reaction_center_properties), config
|
|
56
|
+
)
|
|
57
|
+
# Validated once before spawning; each worker receives private arrays and
|
|
58
|
+
# attaches its own library handle. Side-group completeness is not re-inferred.
|
|
59
|
+
prepared = (*prepared_data, ctypes.CDLL(str(Path(library).resolve())))
|
|
60
|
+
rg, _pg, ro, po = prepared[4:8]
|
|
61
|
+
accumulator = OrbitAccumulator(
|
|
62
|
+
observer,
|
|
63
|
+
rg[1:],
|
|
64
|
+
ro,
|
|
65
|
+
po,
|
|
66
|
+
library_path=library,
|
|
67
|
+
record_only=True,
|
|
68
|
+
)
|
|
69
|
+
_STATE = (
|
|
70
|
+
lgp,
|
|
71
|
+
target,
|
|
72
|
+
config,
|
|
73
|
+
seed,
|
|
74
|
+
library,
|
|
75
|
+
counter,
|
|
76
|
+
deadline,
|
|
77
|
+
barrier,
|
|
78
|
+
accumulator,
|
|
79
|
+
prepared,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _ready():
|
|
84
|
+
barrier = _STATE[7]
|
|
85
|
+
barrier.wait(timeout=30)
|
|
86
|
+
barrier.wait(timeout=30)
|
|
87
|
+
return _ADDRESS_SPACE_LIMIT
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _slice(prefixes, slice_nodes): # noqa: C901
|
|
91
|
+
from ....reporting.analysis import _mapping_key
|
|
92
|
+
|
|
93
|
+
lgp, target, config, _seed, library, counter, deadline, _, accumulator, prepared = (
|
|
94
|
+
_STATE
|
|
95
|
+
)
|
|
96
|
+
records, hashes = {}, set()
|
|
97
|
+
# The parent fixes this value before releasing the readiness barrier and
|
|
98
|
+
# never mutates it during search. Snapshot it once per slice: acquiring a
|
|
99
|
+
# shared process lock at every candidate serializes otherwise independent
|
|
100
|
+
# callbacks without improving deadline enforcement.
|
|
101
|
+
slice_deadline = deadline.value
|
|
102
|
+
counter_value = counter.get_obj()
|
|
103
|
+
profile = bool(os.environ.get("SYNKIT_NATIVE_PROFILE"))
|
|
104
|
+
before_timings = dict(accumulator.its_canonicalizer.timings) if profile else {}
|
|
105
|
+
callback_seconds = identifier_seconds = 0.0
|
|
106
|
+
pattern_hits_before = accumulator.pattern_hits
|
|
107
|
+
|
|
108
|
+
def receive_impl(mapping, cost):
|
|
109
|
+
nonlocal identifier_seconds
|
|
110
|
+
if time.perf_counter() >= slice_deadline:
|
|
111
|
+
raise NativeEnumerationStop("time_limit")
|
|
112
|
+
hashes.add(_mapping_key(mapping))
|
|
113
|
+
before = len(accumulator.seen)
|
|
114
|
+
accumulator.observe(mapping, cost)
|
|
115
|
+
if len(accumulator.seen) == before:
|
|
116
|
+
return
|
|
117
|
+
key = next(reversed(accumulator.seen))
|
|
118
|
+
with counter.get_lock():
|
|
119
|
+
if config.max_mappings is not None and counter_value.value >= config.max_mappings:
|
|
120
|
+
accumulator.seen.pop(key)
|
|
121
|
+
raise NativeEnumerationStop("mapping_limit")
|
|
122
|
+
counter_value.value += 1
|
|
123
|
+
accumulator.its_canonicalizer.remember_pattern()
|
|
124
|
+
# IDs depend only on exact certificates, so formatting can run while
|
|
125
|
+
# other workers enumerate. Cached fields never participate in equality.
|
|
126
|
+
identifier_started = time.perf_counter() if profile else 0
|
|
127
|
+
object.__setattr__(key, "identifier", compact_code_identifier(key))
|
|
128
|
+
template_key = accumulator.seen[key][3]
|
|
129
|
+
if template_key is not None:
|
|
130
|
+
object.__setattr__(
|
|
131
|
+
template_key, "identifier", compact_code_identifier(template_key)
|
|
132
|
+
)
|
|
133
|
+
if profile:
|
|
134
|
+
identifier_seconds += time.perf_counter() - identifier_started
|
|
135
|
+
value = accumulator.seen[key]
|
|
136
|
+
records[transport_canonical_key(key)] = (
|
|
137
|
+
*value[:3],
|
|
138
|
+
transport_canonical_key(template_key),
|
|
139
|
+
*value[4:],
|
|
140
|
+
)
|
|
141
|
+
# Membership alone is sufficient to deduplicate subsequent slices.
|
|
142
|
+
accumulator.seen[key] = None
|
|
143
|
+
|
|
144
|
+
def profiled_receive(mapping, cost):
|
|
145
|
+
nonlocal callback_seconds
|
|
146
|
+
started = time.perf_counter()
|
|
147
|
+
try:
|
|
148
|
+
return receive_impl(mapping, cost)
|
|
149
|
+
finally:
|
|
150
|
+
callback_seconds += time.perf_counter() - started
|
|
151
|
+
|
|
152
|
+
receive = profiled_receive if profile else receive_impl
|
|
153
|
+
remaining = deque(prefixes)
|
|
154
|
+
frontier = []
|
|
155
|
+
totals = {
|
|
156
|
+
"candidate_count": 0,
|
|
157
|
+
"visited_nodes": 0,
|
|
158
|
+
"visited_leaves": 0,
|
|
159
|
+
"pruned": 0,
|
|
160
|
+
"prefix_replay_nodes": 0,
|
|
161
|
+
"new_search_nodes": 0,
|
|
162
|
+
}
|
|
163
|
+
batch_started = time.perf_counter()
|
|
164
|
+
reason = None
|
|
165
|
+
while remaining:
|
|
166
|
+
batched = hasattr(prepared[-1], "synkit_distance_frontier_batch")
|
|
167
|
+
options = {}
|
|
168
|
+
node_budget = slice_nodes
|
|
169
|
+
if batched:
|
|
170
|
+
options["prefixes"] = tuple(remaining)
|
|
171
|
+
node_budget *= min(4, len(remaining))
|
|
172
|
+
remaining.clear()
|
|
173
|
+
prefix = ()
|
|
174
|
+
else:
|
|
175
|
+
prefix = remaining.popleft()
|
|
176
|
+
result = enumerate_native_candidates(
|
|
177
|
+
lgp,
|
|
178
|
+
target,
|
|
179
|
+
library_path=library,
|
|
180
|
+
time_limit_seconds=max(0, slice_deadline - time.perf_counter()),
|
|
181
|
+
max_mappings=None,
|
|
182
|
+
callback=receive,
|
|
183
|
+
prefix=prefix,
|
|
184
|
+
slice_nodes=node_budget,
|
|
185
|
+
_prepared=prepared,
|
|
186
|
+
**options,
|
|
187
|
+
)
|
|
188
|
+
for name in totals:
|
|
189
|
+
totals[name] += result[name] or 0
|
|
190
|
+
frontier.extend(result["frontier"])
|
|
191
|
+
if result["reason"] not in (None, "work_slice"):
|
|
192
|
+
reason = result["reason"]
|
|
193
|
+
break
|
|
194
|
+
if time.perf_counter() - batch_started >= 0.25:
|
|
195
|
+
break
|
|
196
|
+
frontier.extend(remaining)
|
|
197
|
+
if reason is None and frontier:
|
|
198
|
+
reason = "work_slice"
|
|
199
|
+
totals.update(
|
|
200
|
+
complete=reason is None,
|
|
201
|
+
reason=reason,
|
|
202
|
+
frontier=frontier,
|
|
203
|
+
retained_classes=len(records),
|
|
204
|
+
pattern_cache_hits=accumulator.pattern_hits - pattern_hits_before,
|
|
205
|
+
elapsed_seconds=time.perf_counter() - batch_started,
|
|
206
|
+
)
|
|
207
|
+
if profile:
|
|
208
|
+
totals["profile"] = {
|
|
209
|
+
key: value - before_timings.get(key, 0)
|
|
210
|
+
for key, value in accumulator.its_canonicalizer.timings.items()
|
|
211
|
+
}
|
|
212
|
+
totals["profile"].update(
|
|
213
|
+
callback_seconds=callback_seconds,
|
|
214
|
+
identifier_seconds=identifier_seconds,
|
|
215
|
+
other_batch_seconds=totals["elapsed_seconds"] - callback_seconds,
|
|
216
|
+
)
|
|
217
|
+
return totals, records, hashes
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def frontier_orbit_search( # noqa: C901
|
|
221
|
+
lgp,
|
|
222
|
+
target,
|
|
223
|
+
config,
|
|
224
|
+
seed,
|
|
225
|
+
observer,
|
|
226
|
+
*,
|
|
227
|
+
library_path,
|
|
228
|
+
workers=8,
|
|
229
|
+
slice_nodes=8192,
|
|
230
|
+
absolute_deadline=None,
|
|
231
|
+
):
|
|
232
|
+
"""Consume every emitted subtree before claiming complete coverage.
|
|
233
|
+
|
|
234
|
+
The shared cap counts unique records retained within each worker, including
|
|
235
|
+
duplicate classes held by different workers. Prefix replay is deterministic
|
|
236
|
+
and preserves the original two-sided pruning state.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
if (
|
|
240
|
+
isinstance(workers, bool)
|
|
241
|
+
or not isinstance(workers, int)
|
|
242
|
+
or workers < 1
|
|
243
|
+
or isinstance(slice_nodes, bool)
|
|
244
|
+
or not isinstance(slice_nodes, int)
|
|
245
|
+
or not 0 < slice_nodes < (1 << 63)
|
|
246
|
+
):
|
|
247
|
+
raise ValueError("workers and slice_nodes must be positive integers")
|
|
248
|
+
if (
|
|
249
|
+
config.binary
|
|
250
|
+
or set(config.symmetry_node_properties)
|
|
251
|
+
!= set(config.reaction_center_properties)
|
|
252
|
+
or config.time_limit_seconds is None
|
|
253
|
+
or not math.isfinite(config.time_limit_seconds)
|
|
254
|
+
):
|
|
255
|
+
raise ValueError(
|
|
256
|
+
"frontier search requires weighted aligned properties and a finite deadline"
|
|
257
|
+
)
|
|
258
|
+
if not hasattr(
|
|
259
|
+
ctypes.CDLL(str(Path(library_path).resolve())), "synkit_distance_frontier"
|
|
260
|
+
):
|
|
261
|
+
raise ValueError(
|
|
262
|
+
"native library lacks frontier support; rebuild it or use the static scheduler"
|
|
263
|
+
)
|
|
264
|
+
prepared = prepare_native_candidates(
|
|
265
|
+
lgp,
|
|
266
|
+
target,
|
|
267
|
+
library_path=library_path,
|
|
268
|
+
initial_mapping=seed,
|
|
269
|
+
node_properties=config.symmetry_node_properties,
|
|
270
|
+
)
|
|
271
|
+
rg, _pg, ro, po = prepared[4:8]
|
|
272
|
+
merged = OrbitAccumulator(
|
|
273
|
+
observer,
|
|
274
|
+
rg[1:],
|
|
275
|
+
ro,
|
|
276
|
+
po,
|
|
277
|
+
library_path=library_path,
|
|
278
|
+
transport_keys=True,
|
|
279
|
+
)
|
|
280
|
+
context = mp.get_context("spawn")
|
|
281
|
+
counter, deadline = context.Value("q", 0), context.Value("d", 0)
|
|
282
|
+
cpu_index = context.Value("i", 0)
|
|
283
|
+
barrier = context.Barrier(workers + 1)
|
|
284
|
+
cpus = worker_cpus(workers)
|
|
285
|
+
started = time.perf_counter()
|
|
286
|
+
coordinator_cpu_started = time.process_time()
|
|
287
|
+
merge_seconds = wait_seconds = 0.0
|
|
288
|
+
pending, running = deque([()]), set()
|
|
289
|
+
records, results, reasons = {}, [], set()
|
|
290
|
+
peak_pending = 1
|
|
291
|
+
profile_totals = {}
|
|
292
|
+
with ProcessPoolExecutor(
|
|
293
|
+
max_workers=workers,
|
|
294
|
+
mp_context=context,
|
|
295
|
+
initializer=_init,
|
|
296
|
+
initargs=(
|
|
297
|
+
lgp,
|
|
298
|
+
target,
|
|
299
|
+
config,
|
|
300
|
+
seed,
|
|
301
|
+
str(library_path),
|
|
302
|
+
counter,
|
|
303
|
+
deadline,
|
|
304
|
+
barrier,
|
|
305
|
+
cpu_index,
|
|
306
|
+
cpus,
|
|
307
|
+
prepared[:-1],
|
|
308
|
+
),
|
|
309
|
+
) as pool:
|
|
310
|
+
ready = [pool.submit(_ready) for _ in range(workers)]
|
|
311
|
+
barrier.wait(timeout=30)
|
|
312
|
+
search_started = time.perf_counter()
|
|
313
|
+
deadline.value = search_started + config.time_limit_seconds
|
|
314
|
+
if absolute_deadline is not None:
|
|
315
|
+
deadline.value = min(deadline.value, absolute_deadline)
|
|
316
|
+
barrier.wait(timeout=30)
|
|
317
|
+
for future in ready:
|
|
318
|
+
future.result()
|
|
319
|
+
while pending or running:
|
|
320
|
+
if time.perf_counter() >= deadline.value:
|
|
321
|
+
reasons.add("time_limit")
|
|
322
|
+
while pending and len(running) < workers and not reasons:
|
|
323
|
+
# Share sparse frontiers across idle workers before batching.
|
|
324
|
+
# Fixed groups of 16 can put all remaining work in one process.
|
|
325
|
+
idle = workers - len(running)
|
|
326
|
+
batch_size = min(64, max(1, len(pending) // idle))
|
|
327
|
+
prefixes = [pending.popleft() for _ in range(batch_size)]
|
|
328
|
+
running.add(pool.submit(_slice, prefixes, slice_nodes))
|
|
329
|
+
if not running:
|
|
330
|
+
break
|
|
331
|
+
wait_started = time.perf_counter()
|
|
332
|
+
finished, running = wait(running, timeout=0.25, return_when=FIRST_COMPLETED)
|
|
333
|
+
merge_started = time.perf_counter()
|
|
334
|
+
wait_seconds += merge_started - wait_started
|
|
335
|
+
for future in finished:
|
|
336
|
+
result, found, hashes = future.result()
|
|
337
|
+
observer.mapping_hashes.update(hashes)
|
|
338
|
+
new_tasks = result.pop("frontier")
|
|
339
|
+
if result["reason"] not in (None, "work_slice"):
|
|
340
|
+
reasons.add(result["reason"])
|
|
341
|
+
else:
|
|
342
|
+
pending.extend(new_tasks)
|
|
343
|
+
peak_pending = max(peak_pending, len(pending))
|
|
344
|
+
for key, value in result.pop("profile", {}).items():
|
|
345
|
+
profile_totals[key] = profile_totals.get(key, 0) + value
|
|
346
|
+
results.append(result)
|
|
347
|
+
for key, value in found.items():
|
|
348
|
+
old = records.get(key)
|
|
349
|
+
if old is not None and (
|
|
350
|
+
old[:2] != value[:2] or old[3:] != value[3:]
|
|
351
|
+
):
|
|
352
|
+
raise RuntimeError(
|
|
353
|
+
"inconsistent exact records across frontier workers"
|
|
354
|
+
)
|
|
355
|
+
if old is None:
|
|
356
|
+
records[key] = value
|
|
357
|
+
merged.merge_record(key, value)
|
|
358
|
+
merge_seconds += time.perf_counter() - merge_started
|
|
359
|
+
merged.finish()
|
|
360
|
+
shutdown_started = time.perf_counter()
|
|
361
|
+
if time.perf_counter() > deadline.value:
|
|
362
|
+
reasons.add("time_limit")
|
|
363
|
+
return {
|
|
364
|
+
"complete": not reasons and not pending,
|
|
365
|
+
"profile_totals": profile_totals,
|
|
366
|
+
"coordinator_cpu_seconds": time.process_time() - coordinator_cpu_started,
|
|
367
|
+
"coordinator_merge_seconds": merge_seconds,
|
|
368
|
+
"coordinator_wait_seconds": wait_seconds,
|
|
369
|
+
"worker_shutdown_seconds": time.perf_counter() - shutdown_started,
|
|
370
|
+
"worker_startup_seconds": search_started - started,
|
|
371
|
+
"reasons": sorted(reasons),
|
|
372
|
+
"timed_phase_seconds": time.perf_counter() - search_started,
|
|
373
|
+
"retained_worker_records": counter.value,
|
|
374
|
+
"candidate_count": sum(item["candidate_count"] for item in results),
|
|
375
|
+
"pattern_cache_hits": sum(item["pattern_cache_hits"] for item in results),
|
|
376
|
+
"mapping_limit_scope": "retained_worker_double_orbit_representatives",
|
|
377
|
+
"workers": workers,
|
|
378
|
+
"scheduler": "resumable_subtree_queue",
|
|
379
|
+
"slice_nodes": slice_nodes,
|
|
380
|
+
"completed_batches": len(results),
|
|
381
|
+
"prefix_replay_nodes": sum(item["prefix_replay_nodes"] for item in results),
|
|
382
|
+
"new_search_nodes": sum(item["new_search_nodes"] for item in results),
|
|
383
|
+
"pending_subtrees": len(pending),
|
|
384
|
+
"peak_pending_subtrees": peak_pending,
|
|
385
|
+
"shards": results,
|
|
386
|
+
"unique_classes": len(records),
|
|
387
|
+
"weighted_product_representatives": observer.count,
|
|
388
|
+
"labeled_mappings": observer.count * merged.product_order,
|
|
389
|
+
"elapsed_seconds": time.perf_counter() - started,
|
|
390
|
+
}, merged
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Experimental exact terminal-H quotient for minimum Lewis mapping classes.
|
|
2
|
+
|
|
3
|
+
For a fixed heavy-atom bijection f, neutral monovalent terminal hydrogens have
|
|
4
|
+
minimum bond cost sum_i |h_R[i] - h_P[f(i)]|. This remains true with unequal H
|
|
5
|
+
inventories when isolated zero-state H placeholders form the missing-side
|
|
6
|
+
reservoir. Adding this term before heavy-map optimization gives a global
|
|
7
|
+
explicit-H Lewis minimum, unlike optimizing heavy bonds and lifting afterward.
|
|
8
|
+
|
|
9
|
+
Only minimum queries use the quotient. Numeric queries, nonordinary H atoms,
|
|
10
|
+
and annotated stereochemistry fall back to the full explicit-H native solver.
|
|
11
|
+
Every optimal integer H transport is lifted, then the common chemical frontend
|
|
12
|
+
performs exact full-ITS classification. This is an experimental reduction, not
|
|
13
|
+
a replacement for the public default or a polynomial all-class enumerator.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import replace
|
|
19
|
+
import time
|
|
20
|
+
|
|
21
|
+
from ....graph.labeled_graph import LabeledGraph
|
|
22
|
+
from ....reaction_search import _map_reaction_with_search
|
|
23
|
+
from ...slap.lap import chemical_distance
|
|
24
|
+
from ..hydrogen import enumerate_minimal_hydrogen_transfers
|
|
25
|
+
from .candidates import NativeEnumerationStop
|
|
26
|
+
from .search import enumerate_cpp_mappings
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Each unsupported hydrogen state needs a distinct, stable fallback reason.
|
|
30
|
+
def _quotient_graph(graph): # noqa: C901
|
|
31
|
+
"""Return a cost-preserving reduced graph and original-index H groups."""
|
|
32
|
+
hydrogen = [i for i, label in enumerate(graph.labels) if label[0] == 1]
|
|
33
|
+
if not hydrogen:
|
|
34
|
+
return None, "no_hydrogen_reduction"
|
|
35
|
+
if any(graph.labels[i] != (1, 0) for i in hydrogen):
|
|
36
|
+
return None, "isotopic_hydrogen"
|
|
37
|
+
if any(graph.props["stereo"]) or any(graph.props["_stereo_anchor"]):
|
|
38
|
+
return None, "annotated_stereochemistry"
|
|
39
|
+
hydrogen_set = set(hydrogen)
|
|
40
|
+
heavy = [i for i in range(len(graph.labels)) if i not in hydrogen_set]
|
|
41
|
+
positions = {atom: i for i, atom in enumerate(heavy)}
|
|
42
|
+
groups = [[] for _ in heavy]
|
|
43
|
+
reservoir = []
|
|
44
|
+
for atom in hydrogen:
|
|
45
|
+
if graph.props["dummy"][atom]:
|
|
46
|
+
if graph.graph[atom]:
|
|
47
|
+
return None, "bonded_hydrogen_placeholder"
|
|
48
|
+
reservoir.append(atom)
|
|
49
|
+
continue
|
|
50
|
+
if graph.props["charge"][atom] or graph.props["radical"][atom]:
|
|
51
|
+
return None, "nonneutral_or_radical_hydrogen"
|
|
52
|
+
neighbors = graph.graph[atom]
|
|
53
|
+
if len(neighbors) != 1:
|
|
54
|
+
return None, "nonterminal_hydrogen"
|
|
55
|
+
parent, weight = next(iter(neighbors.items()))
|
|
56
|
+
if weight != 1 or parent not in positions or graph.labels[parent][0] == 0:
|
|
57
|
+
return None, "nonordinary_hydrogen_bond"
|
|
58
|
+
groups[positions[parent]].append(atom)
|
|
59
|
+
labels = [graph.labels[i] for i in heavy]
|
|
60
|
+
h_anchor = len(labels)
|
|
61
|
+
labels.append((0, -2))
|
|
62
|
+
adjacency = {
|
|
63
|
+
positions[i]: {positions[j]: weight for j, weight in graph.graph[i].items()
|
|
64
|
+
if j in positions}
|
|
65
|
+
for i in heavy
|
|
66
|
+
}
|
|
67
|
+
adjacency[h_anchor] = {}
|
|
68
|
+
for i, children in enumerate(groups):
|
|
69
|
+
if children:
|
|
70
|
+
adjacency[i][h_anchor] = adjacency[h_anchor][i] = len(children)
|
|
71
|
+
levels = {0} | {value for neighbors in adjacency.values() for value in neighbors.values()}
|
|
72
|
+
if len(levels) > 16:
|
|
73
|
+
return None, "quotient_bond_level_limit"
|
|
74
|
+
reduced = LabeledGraph(adjacency, labels)
|
|
75
|
+
for name in ("charge", "radical", "hcount", "stereo", "dummy", "_stereo_anchor"):
|
|
76
|
+
values = graph.props[name]
|
|
77
|
+
reduced.props[name] = [values[i] for i in heavy] + [
|
|
78
|
+
True if name == "dummy" else "" if name == "stereo" else 0
|
|
79
|
+
]
|
|
80
|
+
return (reduced, heavy, groups, reservoir), None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Quotient search and explicit-H fallback must share the remaining deadline.
|
|
84
|
+
def map_reaction_hydrogen_quotient( # noqa: C901
|
|
85
|
+
reaction,
|
|
86
|
+
*,
|
|
87
|
+
library_path,
|
|
88
|
+
CD="minimal",
|
|
89
|
+
CD_mode="exact",
|
|
90
|
+
balance="strict",
|
|
91
|
+
time_limit_seconds=10.0,
|
|
92
|
+
max_mappings=100_000,
|
|
93
|
+
max_bijections=None,
|
|
94
|
+
max_hydrogen_plans=10_000,
|
|
95
|
+
classification_max_search_nodes=100_000,
|
|
96
|
+
):
|
|
97
|
+
"""Map all minimum Lewis classes through a checked H-leaf quotient.
|
|
98
|
+
|
|
99
|
+
Full graph electron-anchor weights are retained verbatim, so heavy-atom
|
|
100
|
+
nonbonding electrons are never re-estimated after hydrogens are removed.
|
|
101
|
+
Unsupported inputs and numeric CD queries use the full explicit-H engine.
|
|
102
|
+
Assignment-space cardinality and its optional admission cap concern the
|
|
103
|
+
reduced problem; full labeled multiplicities remain explicitly unknown.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
# Some RDKit stereo classes have no CIP label. Preserve the full search
|
|
107
|
+
# contract by declining reduction for every explicit chiral annotation.
|
|
108
|
+
raw_stereo = False
|
|
109
|
+
if isinstance(reaction, str) and ("@" in reaction or "|" in reaction):
|
|
110
|
+
raw_stereo = True
|
|
111
|
+
|
|
112
|
+
def search(graphs, **options):
|
|
113
|
+
elapsed_start = time.perf_counter()
|
|
114
|
+
seconds = options["time_limit_seconds"]
|
|
115
|
+
deadline = None if seconds is None else elapsed_start + seconds
|
|
116
|
+
options.pop("backend")
|
|
117
|
+
if options["CD"] != "minimal":
|
|
118
|
+
result = enumerate_cpp_mappings(graphs, **options)
|
|
119
|
+
statistics = dict(result.backend_statistics or {})
|
|
120
|
+
statistics["hydrogen_quotient_fallback"] = "numeric_distance_query"
|
|
121
|
+
return replace(result, backend_statistics=statistics)
|
|
122
|
+
prepared = [_quotient_graph(graph) for graph in graphs]
|
|
123
|
+
failure = next((reason for _, reason in prepared if reason), None)
|
|
124
|
+
if raw_stereo:
|
|
125
|
+
failure = "annotated_stereochemistry"
|
|
126
|
+
if failure:
|
|
127
|
+
options["time_limit_seconds"] = (
|
|
128
|
+
None if deadline is None else max(0, deadline - time.perf_counter())
|
|
129
|
+
)
|
|
130
|
+
result = enumerate_cpp_mappings(graphs, **options)
|
|
131
|
+
statistics = dict(result.backend_statistics or {})
|
|
132
|
+
statistics["hydrogen_quotient_fallback"] = failure
|
|
133
|
+
return replace(result, backend_statistics=statistics)
|
|
134
|
+
left, right = [value for value, _ in prepared]
|
|
135
|
+
reduced = left[0], right[0]
|
|
136
|
+
callback = options["mapping_callback"]
|
|
137
|
+
observed = plans_observed = 0
|
|
138
|
+
|
|
139
|
+
def remaining():
|
|
140
|
+
return None if deadline is None else max(0, deadline - time.perf_counter())
|
|
141
|
+
|
|
142
|
+
def lift(mapping, cost):
|
|
143
|
+
nonlocal observed, plans_observed
|
|
144
|
+
original_mapping = [-1] * len(graphs[0].labels)
|
|
145
|
+
left_heavy, right_heavy = left[1], right[1]
|
|
146
|
+
for i, atom in enumerate(left_heavy):
|
|
147
|
+
original_mapping[atom] = right_heavy[mapping[i]]
|
|
148
|
+
reactant_groups = [list(group) for group in left[2]] + [list(left[3])]
|
|
149
|
+
product_groups = [list(right[2][mapping[i]]) for i in range(len(left_heavy))]
|
|
150
|
+
product_groups.append(list(right[3]))
|
|
151
|
+
rcounts = [len(group) for group in reactant_groups]
|
|
152
|
+
pcounts = [len(group) for group in product_groups]
|
|
153
|
+
plans = enumerate_minimal_hydrogen_transfers(
|
|
154
|
+
rcounts, pcounts, range(len(rcounts)), max_plans=max_hydrogen_plans,
|
|
155
|
+
time_limit_seconds=remaining(),
|
|
156
|
+
)
|
|
157
|
+
for plan in plans.plans:
|
|
158
|
+
if deadline is not None and time.perf_counter() >= deadline:
|
|
159
|
+
raise NativeEnumerationStop("hydrogen_quotient:time_limit")
|
|
160
|
+
source = [list(group) for group in reactant_groups]
|
|
161
|
+
target = [list(group) for group in product_groups]
|
|
162
|
+
lifted = list(original_mapping)
|
|
163
|
+
|
|
164
|
+
def transfer(i, j, count):
|
|
165
|
+
for atom, image in zip(source[i][:count], target[j][:count]):
|
|
166
|
+
lifted[atom] = image
|
|
167
|
+
del source[i][:count]
|
|
168
|
+
del target[j][:count]
|
|
169
|
+
|
|
170
|
+
for i, count in enumerate(plan.preserved):
|
|
171
|
+
transfer(i, i, count)
|
|
172
|
+
for i, j, count in plan.transfers:
|
|
173
|
+
transfer(i, j, count)
|
|
174
|
+
if sorted(lifted) != list(range(len(lifted))):
|
|
175
|
+
raise RuntimeError("H quotient did not lift a compatible bijection")
|
|
176
|
+
if chemical_distance(graphs, lifted, False) != cost:
|
|
177
|
+
raise RuntimeError("H quotient cost differs from full Lewis cost")
|
|
178
|
+
observed += 1
|
|
179
|
+
callback(lifted, cost)
|
|
180
|
+
if options["max_mappings"] is not None and observed >= options["max_mappings"]:
|
|
181
|
+
raise NativeEnumerationStop("mapping_limit")
|
|
182
|
+
plans_observed += plans.observed_plan_count
|
|
183
|
+
if not plans.complete:
|
|
184
|
+
raise NativeEnumerationStop("hydrogen_quotient:" + plans.truncation_reason)
|
|
185
|
+
|
|
186
|
+
query = dict(options)
|
|
187
|
+
query.update(
|
|
188
|
+
mapping_callback=lift, collect_mappings=False, time_limit_seconds=remaining()
|
|
189
|
+
)
|
|
190
|
+
result = enumerate_cpp_mappings(reduced, **query)
|
|
191
|
+
statistics = dict(result.backend_statistics or {})
|
|
192
|
+
statistics.update(
|
|
193
|
+
hydrogen_quotient=True, full_nodes=len(graphs[0].labels),
|
|
194
|
+
quotient_nodes=len(reduced[0].labels), hydrogen_transport_plans=plans_observed,
|
|
195
|
+
mapping_count_scope="lifted_hydrogen_transport_candidates",
|
|
196
|
+
)
|
|
197
|
+
return replace(
|
|
198
|
+
result, selected_mapping_count=observed,
|
|
199
|
+
selected_labeled_mapping_count=None,
|
|
200
|
+
scope="full_lewis_minimum_via_terminal_hydrogen_quotient",
|
|
201
|
+
backend_statistics=statistics,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return _map_reaction_with_search(
|
|
205
|
+
reaction, search_function=search, backend="cpp", library_path=library_path,
|
|
206
|
+
CD=CD, CD_mode=CD_mode, objective="lewis", hydrogen="explicit", balance=balance,
|
|
207
|
+
time_limit_seconds=time_limit_seconds, max_mappings=max_mappings,
|
|
208
|
+
max_bijections=max_bijections, max_hydrogen_plans=max_hydrogen_plans,
|
|
209
|
+
classification_max_search_nodes=classification_max_search_nodes,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
__all__ = ["map_reaction_hydrogen_quotient"]
|