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,506 @@
|
|
|
1
|
+
"""Exact heavy-atom classes scored by minimum implicit-H Lewis completion.
|
|
2
|
+
|
|
3
|
+
This additive interface never expands hydrogen or enumerates H identities. A
|
|
4
|
+
numeric shell selects heavy maps whose *minimum* H-completion score equals CD;
|
|
5
|
+
it is distinct from projecting an arbitrary full-H exact shell onto heavy atoms.
|
|
6
|
+
Literal H atoms remain in rendered endpoints, with no identity map numbers.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import base64
|
|
12
|
+
import math
|
|
13
|
+
import re
|
|
14
|
+
import time
|
|
15
|
+
from collections import Counter
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from numbers import Integral, Real
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
from rdkit import Chem
|
|
21
|
+
|
|
22
|
+
from synkit.IO.mol_to_graph import MolToGraph
|
|
23
|
+
|
|
24
|
+
from ..exact.distance_search.records import (
|
|
25
|
+
DistanceEnumerationResult,
|
|
26
|
+
normalize_distance_target,
|
|
27
|
+
)
|
|
28
|
+
from ...reaction_search import ReactionMapping, ReactionMappingResult
|
|
29
|
+
from ..slap.lap import _adjacency_and_elements
|
|
30
|
+
from .classification import NativeITSBatchClassifier
|
|
31
|
+
from .core import enumerate_core_mappings
|
|
32
|
+
from .implicit_preparation import (
|
|
33
|
+
UnsupportedImplicitHydrogen,
|
|
34
|
+
prepare_implicit_problem,
|
|
35
|
+
)
|
|
36
|
+
from .reaction_text import split_reaction_fields
|
|
37
|
+
from .runtime import resolve_library_path
|
|
38
|
+
from .stereo import assign_stereo, stereo_smiles
|
|
39
|
+
|
|
40
|
+
SCOPE = "heavyclasses_minimum_H_completion_score"
|
|
41
|
+
_PROPERTIES = ("charge", "radical", "hcount", "stereo", "dummy")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class ImplicitReactionMapping(ReactionMapping):
|
|
46
|
+
"""One heavy ITS class, with its actual native equality certificate."""
|
|
47
|
+
|
|
48
|
+
canonical_key_base64: str
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class PreparedImplicitReaction:
|
|
53
|
+
"""Heavy graphs, original molecules and a fixed doubled-distance offset.
|
|
54
|
+
|
|
55
|
+
Graph order is real heavy atoms, typed heavy dummies, electron anchor, H
|
|
56
|
+
count anchor. ``heavy_indices`` addresses the unexpanded endpoint molecules.
|
|
57
|
+
Anchor vertices participate in scoring and certificates, never in SMILES.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
mols: tuple
|
|
61
|
+
graphs: tuple
|
|
62
|
+
heavy_indices: tuple[tuple[int, ...], tuple[int, ...]]
|
|
63
|
+
offset2: int
|
|
64
|
+
metadata: dict
|
|
65
|
+
edge_stereo: tuple
|
|
66
|
+
matrices2: tuple
|
|
67
|
+
agents: str
|
|
68
|
+
reaction: str
|
|
69
|
+
|
|
70
|
+
def classifier(self, library_path, *, identifier_mode="legacy"):
|
|
71
|
+
return make_implicit_classifier(
|
|
72
|
+
self, library_path=library_path, identifier_mode=identifier_mode
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def prepare_implicit_reaction(reaction, *, balance="dummy"):
|
|
77
|
+
"""Prepare the unexpanded model, discarding all input identity map tags.
|
|
78
|
+
|
|
79
|
+
Unsupported H chemistry raises ``UnsupportedImplicitHydrogen``; there is no
|
|
80
|
+
hidden explicit-H fallback. Charge, radicals, total H counts, map-independent
|
|
81
|
+
CIP labels and heavy bond E/Z remain exact class attributes.
|
|
82
|
+
"""
|
|
83
|
+
if not isinstance(reaction, str):
|
|
84
|
+
raise TypeError("reaction must be reaction SMILES")
|
|
85
|
+
parts = split_reaction_fields(
|
|
86
|
+
reaction,
|
|
87
|
+
error_message="Expected reactants>agents>products or reactants>>products",
|
|
88
|
+
)
|
|
89
|
+
if not parts[0] or not parts[2]:
|
|
90
|
+
raise ValueError("Expected reactants>agents>products or reactants>>products")
|
|
91
|
+
params = Chem.SmilesParserParams()
|
|
92
|
+
params.removeHs = False
|
|
93
|
+
cleaned = []
|
|
94
|
+
for side in (parts[0], parts[2]):
|
|
95
|
+
mol = Chem.MolFromSmiles(side, params)
|
|
96
|
+
if mol is None:
|
|
97
|
+
raise ValueError("Could not parse reaction endpoint")
|
|
98
|
+
for atom in mol.GetAtoms():
|
|
99
|
+
atom.SetAtomMapNum(0)
|
|
100
|
+
# Remove only the parsed atom-map field in-place. A SMILES writer can
|
|
101
|
+
# reorder branches even with canonical=False; retaining the input
|
|
102
|
+
# traversal keeps reference heavy indices aligned with this model.
|
|
103
|
+
cleaned.append(re.sub(r":\d+(?=\])", "", side))
|
|
104
|
+
mapless = f"{cleaned[0]}>{parts[1]}>{cleaned[1]}"
|
|
105
|
+
graphs, offset2, metadata = prepare_implicit_problem(mapless, balance=balance)
|
|
106
|
+
mols = tuple(Chem.MolFromSmiles(side, params) for side in cleaned)
|
|
107
|
+
heavy_indices = tuple(
|
|
108
|
+
tuple(metadata[f"heavy_original_indices_{side}"]) for side in ("r", "p")
|
|
109
|
+
)
|
|
110
|
+
n = len(graphs[0].labels)
|
|
111
|
+
stereo_matrices = []
|
|
112
|
+
for mol, graph, indices in zip(mols, graphs, heavy_indices):
|
|
113
|
+
assign_stereo(mol)
|
|
114
|
+
atoms = [mol.GetAtomWithIdx(i) for i in indices]
|
|
115
|
+
pad = n - len(atoms)
|
|
116
|
+
graph.props.update(
|
|
117
|
+
charge=[a.GetFormalCharge() for a in atoms] + [0] * pad,
|
|
118
|
+
radical=[a.GetNumRadicalElectrons() for a in atoms] + [0] * pad,
|
|
119
|
+
hcount=[MolToGraph._total_h_count(a) for a in atoms] + [0] * pad,
|
|
120
|
+
stereo=[
|
|
121
|
+
a.GetProp("_CIPCode") if a.HasProp("_CIPCode") else "" for a in atoms
|
|
122
|
+
]
|
|
123
|
+
+ [""] * pad,
|
|
124
|
+
dummy=[False] * len(atoms) + [True] * pad,
|
|
125
|
+
_stereo_anchor=[0] * n,
|
|
126
|
+
)
|
|
127
|
+
positions = {original: i for i, original in enumerate(indices)}
|
|
128
|
+
edges = np.full((n, n), "", dtype=object)
|
|
129
|
+
for bond in mol.GetBonds():
|
|
130
|
+
left, right = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
|
|
131
|
+
if left not in positions or right not in positions:
|
|
132
|
+
continue
|
|
133
|
+
i, j = positions[left], positions[right]
|
|
134
|
+
edges[i, j] = edges[j, i] = str(bond.GetStereo())
|
|
135
|
+
if bond.GetStereo() != Chem.BondStereo.STEREONONE:
|
|
136
|
+
graph.props["_stereo_anchor"][i] = i + 1
|
|
137
|
+
graph.props["_stereo_anchor"][j] = j + 1
|
|
138
|
+
stereo_matrices.append(edges)
|
|
139
|
+
colors = {"": 0}
|
|
140
|
+
for graph, edges in zip(graphs, stereo_matrices):
|
|
141
|
+
graph.props["_native_edge_colors"] = np.asarray(
|
|
142
|
+
[[colors.setdefault(value, len(colors)) for value in row] for row in edges],
|
|
143
|
+
dtype=np.int32,
|
|
144
|
+
)
|
|
145
|
+
matrices2 = tuple(
|
|
146
|
+
np.rint(_adjacency_and_elements(g, False)[0] * 2).astype(np.int64)
|
|
147
|
+
for g in graphs
|
|
148
|
+
)
|
|
149
|
+
return PreparedImplicitReaction(
|
|
150
|
+
mols,
|
|
151
|
+
graphs,
|
|
152
|
+
heavy_indices,
|
|
153
|
+
offset2,
|
|
154
|
+
{
|
|
155
|
+
**metadata,
|
|
156
|
+
"scope": SCOPE,
|
|
157
|
+
"class_scope": SCOPE,
|
|
158
|
+
"stereo_assignment": "modern_CIP_v1; supplied_geometry_preserved",
|
|
159
|
+
},
|
|
160
|
+
tuple(stereo_matrices),
|
|
161
|
+
matrices2,
|
|
162
|
+
parts[1],
|
|
163
|
+
mapless,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def make_implicit_classifier(prepared, *, library_path, identifier_mode="legacy"):
|
|
168
|
+
"""Construct the exact heavy ITS palette used by returned certificates."""
|
|
169
|
+
a, elements = _adjacency_and_elements(prepared.graphs[0], False)
|
|
170
|
+
b, _ = _adjacency_and_elements(prepared.graphs[1], False)
|
|
171
|
+
return NativeITSBatchClassifier(
|
|
172
|
+
a,
|
|
173
|
+
b,
|
|
174
|
+
elements,
|
|
175
|
+
{name: tuple(g.props[name] for g in prepared.graphs) for name in _PROPERTIES},
|
|
176
|
+
library_path=library_path,
|
|
177
|
+
edge_properties={"stereo": prepared.edge_stereo},
|
|
178
|
+
identifier_mode=identifier_mode,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def score_implicit_mapping2(prepared, mapping, *, include_offset=True):
|
|
183
|
+
"""Independently rescore a complete typed map, returning doubled CD."""
|
|
184
|
+
n = len(prepared.graphs[0].labels)
|
|
185
|
+
mapping = tuple(mapping)
|
|
186
|
+
if (
|
|
187
|
+
len(mapping) != n
|
|
188
|
+
or any(isinstance(j, bool) or not isinstance(j, Integral) for j in mapping)
|
|
189
|
+
or sorted(mapping) != list(range(n))
|
|
190
|
+
):
|
|
191
|
+
raise ValueError("Expected a complete integer bijection")
|
|
192
|
+
if any(
|
|
193
|
+
prepared.graphs[0].labels[i] != prepared.graphs[1].labels[j]
|
|
194
|
+
for i, j in enumerate(mapping)
|
|
195
|
+
):
|
|
196
|
+
raise ValueError("Mapping violates atom types")
|
|
197
|
+
a, b = prepared.matrices2
|
|
198
|
+
cost2 = int(np.abs(a - b[np.ix_(mapping, mapping)]).sum() // 2)
|
|
199
|
+
return cost2 + (prepared.offset2 if include_offset else 0)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _render_reactant(prepared, *, canonical_output=True):
|
|
203
|
+
mol = Chem.Mol(prepared.mols[0])
|
|
204
|
+
for i, original in enumerate(prepared.heavy_indices[0]):
|
|
205
|
+
mol.GetAtomWithIdx(original).SetAtomMapNum(i + 1)
|
|
206
|
+
return stereo_smiles(mol, canonical=canonical_output)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _render(prepared, mapping, *, canonical_output=True, reactant_text=None):
|
|
210
|
+
product = Chem.Mol(prepared.mols[1])
|
|
211
|
+
left, right = prepared.heavy_indices
|
|
212
|
+
inverse = {j: i for i, j in enumerate(mapping)}
|
|
213
|
+
for j, original in enumerate(right):
|
|
214
|
+
product.GetAtomWithIdx(original).SetAtomMapNum(inverse[j] + 1)
|
|
215
|
+
reactant_only = tuple(i + 1 for i in range(len(left)) if mapping[i] >= len(right))
|
|
216
|
+
product_only = tuple(
|
|
217
|
+
inverse[j] + 1 for j in range(len(right)) if inverse[j] >= len(left)
|
|
218
|
+
)
|
|
219
|
+
if reactant_text is None:
|
|
220
|
+
reactant_text = _render_reactant(prepared, canonical_output=canonical_output)
|
|
221
|
+
product_text = stereo_smiles(product, canonical=canonical_output)
|
|
222
|
+
return (
|
|
223
|
+
f"{reactant_text}>{prepared.agents}>{product_text}",
|
|
224
|
+
reactant_only,
|
|
225
|
+
product_only,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _empty_search(target, backend, reason=None, *, complete=False, unsupported=False):
|
|
230
|
+
return DistanceEnumerationResult(
|
|
231
|
+
target=target,
|
|
232
|
+
cost=None,
|
|
233
|
+
minimum_cost=None,
|
|
234
|
+
mappings=[],
|
|
235
|
+
distances=[],
|
|
236
|
+
total_bijections=0,
|
|
237
|
+
maximum_cost_upper_bound=0,
|
|
238
|
+
visited_leaves=0,
|
|
239
|
+
pruned_branches=0,
|
|
240
|
+
elapsed_seconds=0,
|
|
241
|
+
status="no_solutions"
|
|
242
|
+
if complete
|
|
243
|
+
else "unsupported"
|
|
244
|
+
if unsupported
|
|
245
|
+
else "timeout",
|
|
246
|
+
complete=complete,
|
|
247
|
+
truncation_reason=reason,
|
|
248
|
+
backend=f"{backend}_cpp",
|
|
249
|
+
backend_statistics={
|
|
250
|
+
"minimum_proven": 0,
|
|
251
|
+
"minimum2": -1,
|
|
252
|
+
"incumbent2": -1,
|
|
253
|
+
"lower_bound2": -1,
|
|
254
|
+
"search_complete": int(complete),
|
|
255
|
+
"classification_complete": int(complete),
|
|
256
|
+
"candidate_count": 0,
|
|
257
|
+
"class_count": 0,
|
|
258
|
+
"backend_executed": False,
|
|
259
|
+
},
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# Preparation, native search and class rendering consume one shared deadline.
|
|
264
|
+
def map_reaction_implicit( # noqa: C901
|
|
265
|
+
reaction,
|
|
266
|
+
*,
|
|
267
|
+
library_path=None,
|
|
268
|
+
backend="pabs",
|
|
269
|
+
CD="minimal",
|
|
270
|
+
CD_mode="exact",
|
|
271
|
+
balance="dummy",
|
|
272
|
+
time_limit_seconds=None,
|
|
273
|
+
max_mappings=10000,
|
|
274
|
+
max_states=None,
|
|
275
|
+
max_classes=None,
|
|
276
|
+
max_frontier=None,
|
|
277
|
+
memory_limit_bytes=None,
|
|
278
|
+
classification_max_search_nodes=100000,
|
|
279
|
+
symmetry=True,
|
|
280
|
+
proof_only=False,
|
|
281
|
+
cancel_token=None,
|
|
282
|
+
identifier_mode="legacy",
|
|
283
|
+
canonical_output=True,
|
|
284
|
+
):
|
|
285
|
+
"""Enumerate exact heavy classes with minimum implicit-H Lewis scores.
|
|
286
|
+
|
|
287
|
+
Preparation, native search, classification and rendering share one deadline.
|
|
288
|
+
``CD='minimal'`` proves the minimum and enumerates its heavy classes unless
|
|
289
|
+
``proof_only=True``. Numeric exact/at-most queries do no extra minimum solve.
|
|
290
|
+
Unsupported H models return an explicit unsupported result, with no H lift.
|
|
291
|
+
``canonical_key_base64`` is the actual native packed equality key; class IDs
|
|
292
|
+
are presentation identifiers and are never used for deduplication.
|
|
293
|
+
"""
|
|
294
|
+
started = time.perf_counter()
|
|
295
|
+
if not isinstance(canonical_output, bool):
|
|
296
|
+
raise TypeError("canonical_output must be boolean")
|
|
297
|
+
if identifier_mode not in {"legacy", "packed"}:
|
|
298
|
+
raise ValueError("identifier_mode must be 'legacy' or 'packed'")
|
|
299
|
+
target = normalize_distance_target(CD)
|
|
300
|
+
if CD_mode not in {"exact", "at_most"} or (
|
|
301
|
+
CD_mode == "at_most" and target == "minimal"
|
|
302
|
+
):
|
|
303
|
+
raise ValueError("at_most requires numeric CD; otherwise use exact")
|
|
304
|
+
if not isinstance(proof_only, bool) or not isinstance(symmetry, bool):
|
|
305
|
+
raise TypeError("proof_only and symmetry must be boolean")
|
|
306
|
+
if proof_only and target != "minimal":
|
|
307
|
+
raise ValueError("proof_only requires minimal CD")
|
|
308
|
+
if time_limit_seconds is not None and (
|
|
309
|
+
isinstance(time_limit_seconds, bool)
|
|
310
|
+
or not isinstance(time_limit_seconds, Real)
|
|
311
|
+
or not math.isfinite(time_limit_seconds)
|
|
312
|
+
or time_limit_seconds < 0
|
|
313
|
+
):
|
|
314
|
+
raise ValueError("time_limit_seconds must be finite and nonnegative")
|
|
315
|
+
if classification_max_search_nodes is not None and (
|
|
316
|
+
isinstance(classification_max_search_nodes, bool)
|
|
317
|
+
or not isinstance(classification_max_search_nodes, Integral)
|
|
318
|
+
or classification_max_search_nodes < 1
|
|
319
|
+
):
|
|
320
|
+
raise ValueError("classification_max_search_nodes must be positive or None")
|
|
321
|
+
library_path = resolve_library_path(library_path)
|
|
322
|
+
deadline = None if time_limit_seconds is None else started + time_limit_seconds
|
|
323
|
+
|
|
324
|
+
def remaining():
|
|
325
|
+
return None if deadline is None else max(0, deadline - time.perf_counter())
|
|
326
|
+
|
|
327
|
+
records, registry = {}, {}
|
|
328
|
+
reactant_text = None
|
|
329
|
+
rendering_complete = True
|
|
330
|
+
preparation_reason = None
|
|
331
|
+
prepared = None
|
|
332
|
+
try:
|
|
333
|
+
prepared = prepare_implicit_reaction(reaction, balance=balance)
|
|
334
|
+
except UnsupportedImplicitHydrogen as error:
|
|
335
|
+
preparation_reason = f"implicit_H_unsupported:{error.reason}"
|
|
336
|
+
if prepared is None:
|
|
337
|
+
search = _empty_search(target, backend, preparation_reason, unsupported=True)
|
|
338
|
+
else:
|
|
339
|
+
reduced_target = (
|
|
340
|
+
target if target == "minimal" else target - prepared.offset2 / 2
|
|
341
|
+
)
|
|
342
|
+
if reduced_target != "minimal" and reduced_target < 0:
|
|
343
|
+
search = _empty_search(target, backend, complete=True)
|
|
344
|
+
elif remaining() == 0:
|
|
345
|
+
search = _empty_search(target, backend, "time_limit")
|
|
346
|
+
else:
|
|
347
|
+
classifier = None
|
|
348
|
+
if not proof_only:
|
|
349
|
+
classifier = make_implicit_classifier(
|
|
350
|
+
prepared, library_path=library_path, identifier_mode=identifier_mode
|
|
351
|
+
)
|
|
352
|
+
classifier.frontend_max_canonical_nodes = (
|
|
353
|
+
classification_max_search_nodes
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
def receive(mapping, cost):
|
|
357
|
+
nonlocal rendering_complete, reactant_text
|
|
358
|
+
record = registry.pop(tuple(mapping), None)
|
|
359
|
+
if record is None:
|
|
360
|
+
raise RuntimeError(
|
|
361
|
+
"Native callback lacks its exact class certificate"
|
|
362
|
+
)
|
|
363
|
+
if (
|
|
364
|
+
score_implicit_mapping2(prepared, mapping, include_offset=False)
|
|
365
|
+
!= record.cost2
|
|
366
|
+
or abs(cost * 2 - record.cost2) > 1e-9
|
|
367
|
+
):
|
|
368
|
+
raise RuntimeError(
|
|
369
|
+
"Native implicit mapping failed independent rescoring"
|
|
370
|
+
)
|
|
371
|
+
if record.key.parts in records:
|
|
372
|
+
return
|
|
373
|
+
if deadline is not None and time.perf_counter() >= deadline:
|
|
374
|
+
rendering_complete = False
|
|
375
|
+
return
|
|
376
|
+
if reactant_text is None:
|
|
377
|
+
reactant_text = _render_reactant(
|
|
378
|
+
prepared, canonical_output=canonical_output
|
|
379
|
+
)
|
|
380
|
+
rendered, left_only, right_only = _render(
|
|
381
|
+
prepared,
|
|
382
|
+
mapping,
|
|
383
|
+
canonical_output=canonical_output,
|
|
384
|
+
reactant_text=reactant_text,
|
|
385
|
+
)
|
|
386
|
+
records[record.key.parts] = ImplicitReactionMapping(
|
|
387
|
+
rendered,
|
|
388
|
+
tuple(mapping),
|
|
389
|
+
record.identifier,
|
|
390
|
+
(record.cost2 + prepared.offset2) / 2,
|
|
391
|
+
None,
|
|
392
|
+
left_only,
|
|
393
|
+
right_only,
|
|
394
|
+
base64.b64encode(record.key.parts).decode("ascii"),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
search = enumerate_core_mappings(
|
|
398
|
+
prepared.graphs,
|
|
399
|
+
library_path=library_path,
|
|
400
|
+
backend=backend,
|
|
401
|
+
CD=reduced_target,
|
|
402
|
+
CD_mode=CD_mode,
|
|
403
|
+
symmetry=symmetry,
|
|
404
|
+
node_properties=(*_PROPERTIES, "_stereo_anchor"),
|
|
405
|
+
time_limit_seconds=remaining(),
|
|
406
|
+
max_states=max_states,
|
|
407
|
+
max_mappings=max_mappings,
|
|
408
|
+
max_classes=max_classes,
|
|
409
|
+
max_frontier=max_frontier,
|
|
410
|
+
memory_limit_bytes=memory_limit_bytes,
|
|
411
|
+
collect_mappings=False,
|
|
412
|
+
mapping_callback=None if proof_only else receive,
|
|
413
|
+
proof_only=proof_only,
|
|
414
|
+
cancel_token=cancel_token,
|
|
415
|
+
_native_classifier=classifier,
|
|
416
|
+
_native_class_registry=None if proof_only else registry,
|
|
417
|
+
)
|
|
418
|
+
if not search.backend_statistics.get("backend_executed", False):
|
|
419
|
+
search.total_bijections = math.prod(
|
|
420
|
+
math.factorial(count)
|
|
421
|
+
for count in Counter(prepared.graphs[0].labels).values()
|
|
422
|
+
)
|
|
423
|
+
search.maximum_cost_upper_bound = (
|
|
424
|
+
sum(int(matrix.sum()) for matrix in prepared.matrices2) / 4
|
|
425
|
+
)
|
|
426
|
+
search.target = target
|
|
427
|
+
for field in ("cost", "minimum_cost", "maximum_cost_upper_bound"):
|
|
428
|
+
value = getattr(search, field)
|
|
429
|
+
if value is not None:
|
|
430
|
+
setattr(search, field, value + prepared.offset2 / 2)
|
|
431
|
+
details = search.backend_statistics
|
|
432
|
+
details.update(
|
|
433
|
+
scope=SCOPE,
|
|
434
|
+
class_scope=SCOPE,
|
|
435
|
+
representation="implicit_H_heavy_graph_with_two_score_anchors",
|
|
436
|
+
hydrogen_nodes_added=0,
|
|
437
|
+
hydrogen_vertices_searched=0,
|
|
438
|
+
hydrogen_nodes_enumerated=0,
|
|
439
|
+
hydrogen_enumeration_requested=False,
|
|
440
|
+
H_identity_scope="marginalized; literal H rendered without map tags",
|
|
441
|
+
enumeration_requested=not proof_only,
|
|
442
|
+
CD_mode=CD_mode,
|
|
443
|
+
class_equality="exact_native_packed_key",
|
|
444
|
+
identifier_mode=identifier_mode,
|
|
445
|
+
canonical_output=canonical_output,
|
|
446
|
+
delivered_heavy_classes=len(records),
|
|
447
|
+
rendering_complete=rendering_complete,
|
|
448
|
+
preparation_unsupported_reason=preparation_reason,
|
|
449
|
+
)
|
|
450
|
+
if prepared is not None:
|
|
451
|
+
details["implicit_preparation"] = prepared.metadata
|
|
452
|
+
details["offset2"] = prepared.offset2
|
|
453
|
+
for field in ("minimum2", "incumbent2", "lower_bound2"):
|
|
454
|
+
if field in details:
|
|
455
|
+
details[f"reduced_{field}"] = details[field]
|
|
456
|
+
if details[field] >= 0:
|
|
457
|
+
details[field] += prepared.offset2
|
|
458
|
+
classification_complete = (
|
|
459
|
+
bool(details.get("classification_complete")) and rendering_complete
|
|
460
|
+
)
|
|
461
|
+
failure = preparation_reason
|
|
462
|
+
if not proof_only and not classification_complete and failure is None:
|
|
463
|
+
failure = "classification:" + (
|
|
464
|
+
details.get("classification_reason")
|
|
465
|
+
or ("rendering_time_limit" if not rendering_complete else None)
|
|
466
|
+
or search.truncation_reason
|
|
467
|
+
or "incomplete"
|
|
468
|
+
)
|
|
469
|
+
count = search.selected_labeled_mapping_count
|
|
470
|
+
return ReactionMappingResult(
|
|
471
|
+
reactions=tuple(
|
|
472
|
+
sorted(records.values(), key=lambda item: item.canonical_key_base64)
|
|
473
|
+
),
|
|
474
|
+
complete=search.complete and classification_complete and not proof_only,
|
|
475
|
+
search_complete=search.complete,
|
|
476
|
+
classification_complete=classification_complete,
|
|
477
|
+
hydrogen_complete=True,
|
|
478
|
+
minimum_cost=search.minimum_cost,
|
|
479
|
+
incomplete_reason=failure or search.truncation_reason,
|
|
480
|
+
backend=search.backend,
|
|
481
|
+
hydrogen="implicit_minimum_completion",
|
|
482
|
+
objective=SCOPE,
|
|
483
|
+
balance=balance,
|
|
484
|
+
agents=prepared.agents if prepared else split_reaction_fields(reaction)[1],
|
|
485
|
+
labeled_mapping_count=count,
|
|
486
|
+
labeled_mapping_count_scope=(
|
|
487
|
+
"not_computed_symmetry_pruned_search"
|
|
488
|
+
if count is None
|
|
489
|
+
else "padded_heavy_mapping"
|
|
490
|
+
),
|
|
491
|
+
minimum_cost_scope=SCOPE,
|
|
492
|
+
elapsed_seconds=time.perf_counter() - started,
|
|
493
|
+
search_result=search,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
__all__ = [
|
|
498
|
+
"SCOPE",
|
|
499
|
+
"ImplicitReactionMapping",
|
|
500
|
+
"PreparedImplicitReaction",
|
|
501
|
+
"UnsupportedImplicitHydrogen",
|
|
502
|
+
"make_implicit_classifier",
|
|
503
|
+
"map_reaction_implicit",
|
|
504
|
+
"prepare_implicit_reaction",
|
|
505
|
+
"score_implicit_mapping2",
|
|
506
|
+
]
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""One exact native certificate for a fixed implicit-H heavy mapping."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ctypes as ct
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import replace
|
|
10
|
+
from numbers import Real
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from synkit.Graph.Canon.exact import _encode_color
|
|
16
|
+
|
|
17
|
+
from ...reporting.spectrum import _typed
|
|
18
|
+
from .classification import (
|
|
19
|
+
_INT,
|
|
20
|
+
_LONG,
|
|
21
|
+
NativeClassificationResult,
|
|
22
|
+
_cap,
|
|
23
|
+
_classification_result,
|
|
24
|
+
_load,
|
|
25
|
+
_Problem,
|
|
26
|
+
_records_callback,
|
|
27
|
+
_Statistics,
|
|
28
|
+
)
|
|
29
|
+
from .implicit import _PROPERTIES, score_implicit_mapping2
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def classify_implicit_mapping(
|
|
33
|
+
prepared,
|
|
34
|
+
mapping,
|
|
35
|
+
*,
|
|
36
|
+
library_path,
|
|
37
|
+
identifier_mode="packed",
|
|
38
|
+
time_limit_seconds=None,
|
|
39
|
+
max_canonical_nodes=None,
|
|
40
|
+
cancel_token=None,
|
|
41
|
+
):
|
|
42
|
+
"""Canonicalize only the joint states used by this complete typed mapping.
|
|
43
|
+
|
|
44
|
+
The native canonicalizer and exact certificate bytes are the same as batch
|
|
45
|
+
classification. This prepares O(n + edges) joint color tokens instead of
|
|
46
|
+
every possible atom-state pairing. Dense ABI arrays remain O(n squared).
|
|
47
|
+
No reusable restricted palette or search domains are exposed to callers.
|
|
48
|
+
Preparation and canonicalization share the supplied deadline.
|
|
49
|
+
"""
|
|
50
|
+
started = time.perf_counter()
|
|
51
|
+
if identifier_mode not in {"legacy", "packed"}:
|
|
52
|
+
raise ValueError("identifier_mode must be 'legacy' or 'packed'")
|
|
53
|
+
if time_limit_seconds is not None and (
|
|
54
|
+
isinstance(time_limit_seconds, bool)
|
|
55
|
+
or not isinstance(time_limit_seconds, Real)
|
|
56
|
+
or not math.isfinite(time_limit_seconds)
|
|
57
|
+
or time_limit_seconds < 0
|
|
58
|
+
):
|
|
59
|
+
raise ValueError("time_limit_seconds must be finite and nonnegative")
|
|
60
|
+
node_cap = _cap(max_canonical_nodes, "max_canonical_nodes")
|
|
61
|
+
mapping = tuple(mapping)
|
|
62
|
+
# This independently checks integral IDs, bijectivity and all hard types.
|
|
63
|
+
cost2 = score_implicit_mapping2(prepared, mapping, include_offset=False)
|
|
64
|
+
a, b = prepared.graphs
|
|
65
|
+
n = len(mapping)
|
|
66
|
+
library = _load(str(Path(library_path).resolve()))
|
|
67
|
+
capacity = min(
|
|
68
|
+
512,
|
|
69
|
+
library.synkit_mapper_classification_max_vertices()
|
|
70
|
+
if hasattr(library, "synkit_mapper_classification_max_vertices")
|
|
71
|
+
else 256,
|
|
72
|
+
)
|
|
73
|
+
if not 1 <= n <= capacity:
|
|
74
|
+
raise ValueError(f"Native ITS library supports 1..{capacity} vertices")
|
|
75
|
+
if identifier_mode == "packed" and not hasattr(
|
|
76
|
+
library, "synkit_mapper_classify_batch_ex"
|
|
77
|
+
):
|
|
78
|
+
raise RuntimeError("Native library does not support packed identifiers")
|
|
79
|
+
elements = a.props.get("atomic numbers", a.labels)
|
|
80
|
+
nodes = [
|
|
81
|
+
_encode_color(
|
|
82
|
+
(
|
|
83
|
+
_typed(elements[i]),
|
|
84
|
+
tuple(
|
|
85
|
+
(name, _typed(a.props[name][i]), _typed(b.props[name][j]))
|
|
86
|
+
for name in _PROPERTIES
|
|
87
|
+
),
|
|
88
|
+
(),
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
for i, j in enumerate(mapping)
|
|
92
|
+
]
|
|
93
|
+
inverse = {j: i for i, j in enumerate(mapping)}
|
|
94
|
+
pairs = {
|
|
95
|
+
(i, j)
|
|
96
|
+
for i, row in a.graph.items()
|
|
97
|
+
for j, weight in row.items()
|
|
98
|
+
if i < j and weight
|
|
99
|
+
}
|
|
100
|
+
pairs.update(
|
|
101
|
+
tuple(sorted((inverse[i], inverse[j])))
|
|
102
|
+
for i, row in b.graph.items()
|
|
103
|
+
for j, weight in row.items()
|
|
104
|
+
if i < j and weight
|
|
105
|
+
)
|
|
106
|
+
edges = {
|
|
107
|
+
(i, k): _encode_color(
|
|
108
|
+
(
|
|
109
|
+
float(a.graph.get(i, {}).get(k, 0)),
|
|
110
|
+
float(b.graph.get(mapping[i], {}).get(mapping[k], 0)),
|
|
111
|
+
prepared.edge_stereo[0][i, k],
|
|
112
|
+
prepared.edge_stereo[1][mapping[i], mapping[k]],
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
for i, k in pairs
|
|
116
|
+
}
|
|
117
|
+
tokens = sorted(set(nodes) | set(edges.values()))
|
|
118
|
+
if len(tokens) >= 65536:
|
|
119
|
+
raise ValueError("Native ITS exact palette exceeds uint16")
|
|
120
|
+
ids = {token: i for i, token in enumerate(tokens)}
|
|
121
|
+
node_colors = np.ascontiguousarray(
|
|
122
|
+
np.repeat(
|
|
123
|
+
np.asarray([ids[token] for token in nodes], dtype=np.int32)[:, None],
|
|
124
|
+
n,
|
|
125
|
+
axis=1,
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
edge_matrix = np.zeros((n, n), dtype=np.int32)
|
|
129
|
+
for (i, j), token in edges.items():
|
|
130
|
+
edge_matrix[i, j] = edge_matrix[j, i] = ids[token] + 1
|
|
131
|
+
zero = np.zeros_like(edge_matrix)
|
|
132
|
+
edge_colors = np.arange(len(tokens) + 1, dtype=np.int32)
|
|
133
|
+
palette = (ct.c_char_p * len(tokens))(
|
|
134
|
+
*(json.dumps(token, separators=(",", ":")).encode("ascii") for token in tokens)
|
|
135
|
+
)
|
|
136
|
+
problem = _Problem(
|
|
137
|
+
n,
|
|
138
|
+
node_colors.ctypes.data_as(_INT),
|
|
139
|
+
edge_matrix.ctypes.data_as(_INT),
|
|
140
|
+
zero.ctypes.data_as(_INT),
|
|
141
|
+
len(tokens) + 1,
|
|
142
|
+
1,
|
|
143
|
+
edge_colors.ctypes.data_as(_INT),
|
|
144
|
+
len(tokens),
|
|
145
|
+
palette,
|
|
146
|
+
)
|
|
147
|
+
remaining = (
|
|
148
|
+
None
|
|
149
|
+
if time_limit_seconds is None
|
|
150
|
+
else max(0.0, time_limit_seconds - (time.perf_counter() - started))
|
|
151
|
+
)
|
|
152
|
+
if remaining == 0:
|
|
153
|
+
return NativeClassificationResult(
|
|
154
|
+
(), 0, False, "time_limit", 0, time.perf_counter() - started
|
|
155
|
+
)
|
|
156
|
+
identity = np.arange(n, dtype=np.int32)
|
|
157
|
+
costs = np.asarray([cost2], dtype=np.int64)
|
|
158
|
+
classes, errors = [], []
|
|
159
|
+
receive = _records_callback(n, classes, errors, None)
|
|
160
|
+
statistics = _Statistics()
|
|
161
|
+
function = (
|
|
162
|
+
library.synkit_mapper_classify_batch_ex
|
|
163
|
+
if identifier_mode == "packed"
|
|
164
|
+
else library.synkit_mapper_classify_batch_with_cancel
|
|
165
|
+
)
|
|
166
|
+
status = function(
|
|
167
|
+
ct.byref(problem),
|
|
168
|
+
1,
|
|
169
|
+
identity.ctypes.data_as(_INT),
|
|
170
|
+
costs.ctypes.data_as(_LONG),
|
|
171
|
+
-1 if remaining is None else remaining,
|
|
172
|
+
node_cap,
|
|
173
|
+
-1,
|
|
174
|
+
None if cancel_token is None else cancel_token.pointer,
|
|
175
|
+
receive,
|
|
176
|
+
None,
|
|
177
|
+
ct.byref(statistics),
|
|
178
|
+
*((0, 1) if identifier_mode == "packed" else ()),
|
|
179
|
+
)
|
|
180
|
+
if errors:
|
|
181
|
+
raise errors[0]
|
|
182
|
+
if status:
|
|
183
|
+
raise RuntimeError(library.synkit_mapper_classification_error().decode())
|
|
184
|
+
return replace(
|
|
185
|
+
_classification_result(classes, statistics, time.perf_counter() - started),
|
|
186
|
+
classes=tuple(replace(item, representative=mapping) for item in classes),
|
|
187
|
+
)
|