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,550 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import weakref
|
|
3
|
+
from typing import Any, Dict, Iterable, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
import networkx as nx
|
|
6
|
+
from joblib import Parallel, delayed
|
|
7
|
+
|
|
8
|
+
from synkit.IO import smiles_to_graph, rsmi_to_its
|
|
9
|
+
from .rule_filter import RuleFilter
|
|
10
|
+
from ..core.engine import SynReactor
|
|
11
|
+
|
|
12
|
+
__all__ = ["BatchReactor"]
|
|
13
|
+
|
|
14
|
+
_UNSUPPORTED_CACHE_VALUE = object()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _freeze_cache_value(value: Any, active: set[int] | None = None) -> Any:
|
|
18
|
+
"""Return an exact hashable value, or a sentinel for unsupported state."""
|
|
19
|
+
value_type = type(value)
|
|
20
|
+
if value_type in {str, int, float, bool, bytes, type(None)}:
|
|
21
|
+
return value_type, value
|
|
22
|
+
if active is None:
|
|
23
|
+
active = set()
|
|
24
|
+
value_id = id(value)
|
|
25
|
+
if value_id in active:
|
|
26
|
+
return _UNSUPPORTED_CACHE_VALUE
|
|
27
|
+
active.add(value_id)
|
|
28
|
+
try:
|
|
29
|
+
if isinstance(value, dict):
|
|
30
|
+
records = []
|
|
31
|
+
for key, item in value.items():
|
|
32
|
+
frozen_key = _freeze_cache_value(key, active)
|
|
33
|
+
frozen_item = _freeze_cache_value(item, active)
|
|
34
|
+
if (
|
|
35
|
+
frozen_key is _UNSUPPORTED_CACHE_VALUE
|
|
36
|
+
or frozen_item is _UNSUPPORTED_CACHE_VALUE
|
|
37
|
+
):
|
|
38
|
+
return _UNSUPPORTED_CACHE_VALUE
|
|
39
|
+
records.append((frozen_key, frozen_item))
|
|
40
|
+
return dict, frozenset(records)
|
|
41
|
+
if isinstance(value, list):
|
|
42
|
+
records = tuple(_freeze_cache_value(item, active) for item in value)
|
|
43
|
+
return (
|
|
44
|
+
_UNSUPPORTED_CACHE_VALUE
|
|
45
|
+
if _UNSUPPORTED_CACHE_VALUE in records
|
|
46
|
+
else (list, records)
|
|
47
|
+
)
|
|
48
|
+
if isinstance(value, tuple):
|
|
49
|
+
records = tuple(_freeze_cache_value(item, active) for item in value)
|
|
50
|
+
return (
|
|
51
|
+
_UNSUPPORTED_CACHE_VALUE
|
|
52
|
+
if _UNSUPPORTED_CACHE_VALUE in records
|
|
53
|
+
else (tuple, records)
|
|
54
|
+
)
|
|
55
|
+
if isinstance(value, (set, frozenset)):
|
|
56
|
+
records = tuple(_freeze_cache_value(item, active) for item in value)
|
|
57
|
+
return (
|
|
58
|
+
_UNSUPPORTED_CACHE_VALUE
|
|
59
|
+
if _UNSUPPORTED_CACHE_VALUE in records
|
|
60
|
+
else (value_type, frozenset(records))
|
|
61
|
+
)
|
|
62
|
+
# A lossy repr or identity token could conflate states after object-id
|
|
63
|
+
# reuse. Failing open preserves correctness for third-party objects.
|
|
64
|
+
return _UNSUPPORTED_CACHE_VALUE
|
|
65
|
+
finally:
|
|
66
|
+
active.remove(value_id)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _graph_cache_state(graph: nx.Graph) -> Any:
|
|
70
|
+
"""Capture exact graph topology and supported attribute state."""
|
|
71
|
+
graph_attrs = _freeze_cache_value(graph.graph)
|
|
72
|
+
node_records = []
|
|
73
|
+
for node, attrs in graph.nodes(data=True):
|
|
74
|
+
frozen_node = _freeze_cache_value(node)
|
|
75
|
+
frozen_attrs = _freeze_cache_value(attrs)
|
|
76
|
+
if (
|
|
77
|
+
frozen_node is _UNSUPPORTED_CACHE_VALUE
|
|
78
|
+
or frozen_attrs is _UNSUPPORTED_CACHE_VALUE
|
|
79
|
+
):
|
|
80
|
+
return _UNSUPPORTED_CACHE_VALUE
|
|
81
|
+
node_records.append((frozen_node, frozen_attrs))
|
|
82
|
+
|
|
83
|
+
edge_records = []
|
|
84
|
+
edges = (
|
|
85
|
+
graph.edges(keys=True, data=True)
|
|
86
|
+
if graph.is_multigraph()
|
|
87
|
+
else (
|
|
88
|
+
(left, right, None, attrs) for left, right, attrs in graph.edges(data=True)
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
for left, right, key, attrs in edges:
|
|
92
|
+
frozen = tuple(
|
|
93
|
+
_freeze_cache_value(value) for value in (left, right, key, attrs)
|
|
94
|
+
)
|
|
95
|
+
if _UNSUPPORTED_CACHE_VALUE in frozen:
|
|
96
|
+
return _UNSUPPORTED_CACHE_VALUE
|
|
97
|
+
edge_records.append(frozen)
|
|
98
|
+
if graph_attrs is _UNSUPPORTED_CACHE_VALUE:
|
|
99
|
+
return _UNSUPPORTED_CACHE_VALUE
|
|
100
|
+
return (
|
|
101
|
+
type(graph),
|
|
102
|
+
graph_attrs,
|
|
103
|
+
frozenset(node_records),
|
|
104
|
+
frozenset(edge_records),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# =============================================================================
|
|
109
|
+
# Low-level rule application
|
|
110
|
+
# =============================================================================
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _apply_rule_raw(
|
|
114
|
+
substrate: nx.Graph,
|
|
115
|
+
rule: nx.Graph,
|
|
116
|
+
invert: bool,
|
|
117
|
+
*,
|
|
118
|
+
strategy: str,
|
|
119
|
+
explicit_h: bool,
|
|
120
|
+
implicit_temp: bool,
|
|
121
|
+
) -> List[str]:
|
|
122
|
+
"""Apply one rule graph to a substrate graph using SynReactor.
|
|
123
|
+
|
|
124
|
+
:param substrate: Graph representing the substrate molecule.
|
|
125
|
+
:type substrate: networkx.Graph
|
|
126
|
+
:param rule: Graph representing the reaction rule (ITS template).
|
|
127
|
+
:type rule: networkx.Graph
|
|
128
|
+
:param invert: Whether to apply the rule in reverse.
|
|
129
|
+
:type invert: bool
|
|
130
|
+
:param strategy: Matching strategy passed to SynReactor.
|
|
131
|
+
:type strategy: str
|
|
132
|
+
:param explicit_h: Use explicit hydrogens in SynReactor.
|
|
133
|
+
:type explicit_h: bool
|
|
134
|
+
:param implicit_temp: Use implicit templates in SynReactor.
|
|
135
|
+
:type implicit_temp: bool
|
|
136
|
+
:return: A list of product SMARTS strings or reaction SMILES.
|
|
137
|
+
:rtype: list of str
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
reactor = SynReactor(
|
|
141
|
+
substrate=substrate,
|
|
142
|
+
template=rule,
|
|
143
|
+
invert=invert,
|
|
144
|
+
strategy=strategy,
|
|
145
|
+
explicit_h=explicit_h,
|
|
146
|
+
implicit_temp=implicit_temp,
|
|
147
|
+
)
|
|
148
|
+
return list(getattr(reactor, "smarts_list", getattr(reactor, "smarts", [])))
|
|
149
|
+
|
|
150
|
+
except Exception as exc: # pragma: no cover
|
|
151
|
+
logging.getLogger(__name__).debug(
|
|
152
|
+
"SynReactor failed (invert=%s): %s", invert, exc
|
|
153
|
+
)
|
|
154
|
+
return []
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# =============================================================================
|
|
158
|
+
# Picklable rule-applier with small FIFO cache
|
|
159
|
+
# =============================================================================
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class _RuleApplier:
|
|
163
|
+
"""
|
|
164
|
+
Callable wrapper around `_apply_rule_raw` with a FIFO cache.
|
|
165
|
+
|
|
166
|
+
Entries are indexed by object identity and validated against exact graph
|
|
167
|
+
snapshots before reuse, so graph mutation and Python id reuse cannot
|
|
168
|
+
produce stale results.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
__slots__ = (
|
|
172
|
+
"_strategy",
|
|
173
|
+
"_explicit_h",
|
|
174
|
+
"_implicit_temp",
|
|
175
|
+
"_cache",
|
|
176
|
+
"_cache_max",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def __init__(
|
|
180
|
+
self,
|
|
181
|
+
*,
|
|
182
|
+
strategy: str,
|
|
183
|
+
explicit_h: bool,
|
|
184
|
+
implicit_temp: bool,
|
|
185
|
+
cache_enabled: bool,
|
|
186
|
+
cache_maxsize: int,
|
|
187
|
+
) -> None:
|
|
188
|
+
"""
|
|
189
|
+
Initialize the rule applier.
|
|
190
|
+
|
|
191
|
+
:param strategy: Matching strategy for SynReactor.
|
|
192
|
+
:param explicit_h: Pass explicit hydrogens to SynReactor.
|
|
193
|
+
:param implicit_temp: Pass implicit template flag to SynReactor.
|
|
194
|
+
:param cache_enabled: Whether to enable in-process caching.
|
|
195
|
+
:param cache_maxsize: Maximum cache entries before eviction.
|
|
196
|
+
"""
|
|
197
|
+
self._strategy = strategy
|
|
198
|
+
self._explicit_h = explicit_h
|
|
199
|
+
self._implicit_temp = implicit_temp
|
|
200
|
+
self._cache = ( # type: ignore[var-annotated]
|
|
201
|
+
{} if cache_enabled and cache_maxsize > 0 else None
|
|
202
|
+
)
|
|
203
|
+
self._cache_max = max(0, int(cache_maxsize))
|
|
204
|
+
|
|
205
|
+
def _execute(
|
|
206
|
+
self,
|
|
207
|
+
substrate: nx.Graph,
|
|
208
|
+
rule: nx.Graph,
|
|
209
|
+
inv: bool,
|
|
210
|
+
) -> List[str]:
|
|
211
|
+
"""Directly call `_apply_rule_raw` without caching."""
|
|
212
|
+
return _apply_rule_raw(
|
|
213
|
+
substrate,
|
|
214
|
+
rule,
|
|
215
|
+
inv,
|
|
216
|
+
strategy=self._strategy,
|
|
217
|
+
explicit_h=self._explicit_h,
|
|
218
|
+
implicit_temp=self._implicit_temp,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def __call__(
|
|
222
|
+
self,
|
|
223
|
+
substrate: nx.Graph,
|
|
224
|
+
rule: nx.Graph,
|
|
225
|
+
inv: bool,
|
|
226
|
+
) -> List[str]:
|
|
227
|
+
"""Apply a rule to a substrate, using the cache if enabled.
|
|
228
|
+
|
|
229
|
+
:param substrate: Substrate graph.
|
|
230
|
+
:param rule: Rule graph.
|
|
231
|
+
:param inv: Inversion flag.
|
|
232
|
+
:return: List of reaction outputs.
|
|
233
|
+
"""
|
|
234
|
+
if self._cache is None:
|
|
235
|
+
return self._execute(substrate, rule, inv)
|
|
236
|
+
|
|
237
|
+
key = (id(substrate), id(rule), inv)
|
|
238
|
+
entry = self._cache.get(key)
|
|
239
|
+
substrate_state = _graph_cache_state(substrate)
|
|
240
|
+
rule_state = _graph_cache_state(rule)
|
|
241
|
+
cacheable = (
|
|
242
|
+
substrate_state is not _UNSUPPORTED_CACHE_VALUE
|
|
243
|
+
and rule_state is not _UNSUPPORTED_CACHE_VALUE
|
|
244
|
+
)
|
|
245
|
+
if entry is not None and cacheable:
|
|
246
|
+
substrate_ref, rule_ref, old_substrate_state, old_rule_state, result = entry
|
|
247
|
+
if (
|
|
248
|
+
substrate_ref() is substrate
|
|
249
|
+
and rule_ref() is rule
|
|
250
|
+
and old_substrate_state == substrate_state
|
|
251
|
+
and old_rule_state == rule_state
|
|
252
|
+
):
|
|
253
|
+
return list(result)
|
|
254
|
+
|
|
255
|
+
res = self._execute(substrate, rule, inv)
|
|
256
|
+
if not cacheable:
|
|
257
|
+
return res
|
|
258
|
+
if len(self._cache) >= self._cache_max:
|
|
259
|
+
self._cache.pop(next(iter(self._cache))) # FIFO eviction
|
|
260
|
+
self._cache[key] = (
|
|
261
|
+
weakref.ref(substrate),
|
|
262
|
+
weakref.ref(rule),
|
|
263
|
+
substrate_state,
|
|
264
|
+
rule_state,
|
|
265
|
+
tuple(res),
|
|
266
|
+
)
|
|
267
|
+
return res
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# =============================================================================
|
|
271
|
+
# Helper utilities
|
|
272
|
+
# =============================================================================
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _dedupe(items: Iterable[Any]) -> List[Any]:
|
|
276
|
+
"""Remove duplicates while preserving order."""
|
|
277
|
+
seen: set[Any] = set()
|
|
278
|
+
out: List[Any] = []
|
|
279
|
+
for x in items:
|
|
280
|
+
if x not in seen:
|
|
281
|
+
seen.add(x)
|
|
282
|
+
out.append(x)
|
|
283
|
+
return out
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# =============================================================================
|
|
287
|
+
# Public API
|
|
288
|
+
# =============================================================================
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class BatchReactor:
|
|
292
|
+
"""
|
|
293
|
+
Parallel, cache-aware batch application of reaction rules to SMILES substrates.
|
|
294
|
+
|
|
295
|
+
:param data: List of SMILES strings or dicts containing SMILES under `host_key`.
|
|
296
|
+
:type data: list of str or dict
|
|
297
|
+
:param host_key: Key to extract SMILES from dict entries (optional).
|
|
298
|
+
:type host_key: str or None
|
|
299
|
+
:param pre_filter_engine: Pre-filtering engine for rules (None to skip).
|
|
300
|
+
:type pre_filter_engine: str or None
|
|
301
|
+
:param explicit_h: Use explicit hydrogens in SynReactor.
|
|
302
|
+
:type explicit_h: bool
|
|
303
|
+
:param implicit_temp: Use implicit templates in SynReactor.
|
|
304
|
+
:type implicit_temp: bool
|
|
305
|
+
:param strategy: Matching strategy for SynReactor.
|
|
306
|
+
:type strategy: str
|
|
307
|
+
:param dedupe: Deduplicate results per-substrate.
|
|
308
|
+
:type dedupe: bool
|
|
309
|
+
:param entry_n_jobs: Number of parallel jobs for substrates.
|
|
310
|
+
:type entry_n_jobs: int
|
|
311
|
+
:param rule_n_jobs: Number of parallel jobs for rules per substrate.
|
|
312
|
+
:type rule_n_jobs: int
|
|
313
|
+
:param parallel_rules: Enable parallelism over rules.
|
|
314
|
+
:type parallel_rules: bool
|
|
315
|
+
:param allow_nested: Allow nested parallelism.
|
|
316
|
+
:type allow_nested: bool
|
|
317
|
+
:param cache_enabled: Enable in-process per-rule caching.
|
|
318
|
+
:type cache_enabled: bool
|
|
319
|
+
:param cache_maxsize: Max entries in per-process cache.
|
|
320
|
+
:type cache_maxsize: int
|
|
321
|
+
:param logger: Optional custom logger.
|
|
322
|
+
:type logger: logging.Logger or None
|
|
323
|
+
|
|
324
|
+
:raises ValueError: If SMILES/rule conversion fails.
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
def __init__(
|
|
328
|
+
self,
|
|
329
|
+
data: List[Union[str, Dict[str, Any]]],
|
|
330
|
+
host_key: Optional[str] = None,
|
|
331
|
+
*,
|
|
332
|
+
pre_filter_engine: Optional[str] = None,
|
|
333
|
+
explicit_h: bool = True,
|
|
334
|
+
implicit_temp: bool = False,
|
|
335
|
+
strategy: str = "bt",
|
|
336
|
+
dedupe: bool = True,
|
|
337
|
+
entry_n_jobs: int = 1,
|
|
338
|
+
rule_n_jobs: int = 1,
|
|
339
|
+
parallel_rules: bool = False,
|
|
340
|
+
allow_nested: bool = False,
|
|
341
|
+
cache_enabled: bool = True,
|
|
342
|
+
cache_maxsize: int = 32768,
|
|
343
|
+
logger: Optional[logging.Logger] = None,
|
|
344
|
+
enable_logging: bool = True,
|
|
345
|
+
) -> None:
|
|
346
|
+
"""
|
|
347
|
+
Initialize the BatchReactor.
|
|
348
|
+
|
|
349
|
+
See class docstring for parameter details.
|
|
350
|
+
"""
|
|
351
|
+
self._data = list(data)
|
|
352
|
+
self._host_key = host_key
|
|
353
|
+
self._engine = "syn"
|
|
354
|
+
self._pre_filter = pre_filter_engine.lower() if pre_filter_engine else None
|
|
355
|
+
self._explicit_h = explicit_h
|
|
356
|
+
self._implicit_temp = implicit_temp
|
|
357
|
+
self._strategy = strategy
|
|
358
|
+
self._dedupe = dedupe
|
|
359
|
+
self._entry_jobs = max(1, entry_n_jobs)
|
|
360
|
+
self._rule_jobs = max(1, rule_n_jobs)
|
|
361
|
+
self._parallel_rules = parallel_rules
|
|
362
|
+
self._allow_nested = allow_nested
|
|
363
|
+
self._log = logger or logging.getLogger(__name__)
|
|
364
|
+
if enable_logging is False:
|
|
365
|
+
logging.disable(logging.CRITICAL)
|
|
366
|
+
|
|
367
|
+
self._apply_rule = _RuleApplier(
|
|
368
|
+
strategy=strategy,
|
|
369
|
+
explicit_h=explicit_h,
|
|
370
|
+
implicit_temp=implicit_temp,
|
|
371
|
+
cache_enabled=cache_enabled,
|
|
372
|
+
cache_maxsize=cache_maxsize,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
def help(self) -> str:
|
|
376
|
+
"""Return usage examples and API description.
|
|
377
|
+
|
|
378
|
+
:return: Multi-line help text.
|
|
379
|
+
:rtype: str
|
|
380
|
+
"""
|
|
381
|
+
return (
|
|
382
|
+
"BatchReactor(data, host_key=None, pre_filter_engine=None, "
|
|
383
|
+
"explicit_h=True, implicit_temp=False, strategy='bt', dedupe=True, "
|
|
384
|
+
"entry_n_jobs=1, rule_n_jobs=1, parallel_rules=False, allow_nested=False, "
|
|
385
|
+
"cache_enabled=True, cache_maxsize=32768, logger=None)" + "\n"
|
|
386
|
+
"Use .fit(rules, invert=False) to apply reaction rules to the SMILES batch."
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
def describe(self) -> str:
|
|
390
|
+
"""Return a configuration summary.
|
|
391
|
+
|
|
392
|
+
:return: Human-readable settings overview.
|
|
393
|
+
:rtype: str
|
|
394
|
+
"""
|
|
395
|
+
lines = [
|
|
396
|
+
"BatchReactor configuration:",
|
|
397
|
+
f" entries : {len(self._data)}",
|
|
398
|
+
f" engine : {self._engine}",
|
|
399
|
+
f" pre_filter : {self._pre_filter}",
|
|
400
|
+
f" explicit_h : {self._explicit_h}",
|
|
401
|
+
f" implicit_temp : {self._implicit_temp}",
|
|
402
|
+
f" strategy : {self._strategy}",
|
|
403
|
+
f" dedupe : {self._dedupe}",
|
|
404
|
+
f" entry_n_jobs : {self._entry_jobs}",
|
|
405
|
+
f" rule_n_jobs : {self._rule_jobs}",
|
|
406
|
+
f" parallel_rules : {self._parallel_rules}",
|
|
407
|
+
f" allow_nested : {self._allow_nested}",
|
|
408
|
+
f" cache_enabled : {self._apply_rule._cache is not None}",
|
|
409
|
+
f" cache_maxsize : {self._apply_rule._cache_max}",
|
|
410
|
+
]
|
|
411
|
+
return "\n".join(lines)
|
|
412
|
+
|
|
413
|
+
def __len__(self) -> int:
|
|
414
|
+
"""Return the number of substrates in the batch."""
|
|
415
|
+
return len(self._data)
|
|
416
|
+
|
|
417
|
+
def __iter__(self):
|
|
418
|
+
"""Yield each entry from the batch data."""
|
|
419
|
+
yield from self._data
|
|
420
|
+
|
|
421
|
+
def __getitem__(self, idx: int) -> Union[str, Dict[str, Any]]:
|
|
422
|
+
"""Get the data entry at index `idx`."""
|
|
423
|
+
return self._data[idx]
|
|
424
|
+
|
|
425
|
+
def __repr__(self) -> str:
|
|
426
|
+
"""Concise summary of the BatchReactor instance."""
|
|
427
|
+
return (
|
|
428
|
+
f"<BatchReactor entries={len(self._data)} engine={self._engine} "
|
|
429
|
+
f"pre_filter={self._pre_filter} entry_jobs={self._entry_jobs} "
|
|
430
|
+
f"rule_jobs={self._rule_jobs} parallel_rules={self._parallel_rules}>"
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def fit(
|
|
434
|
+
self,
|
|
435
|
+
rules: Iterable[Any],
|
|
436
|
+
*,
|
|
437
|
+
invert: bool = False,
|
|
438
|
+
) -> List[Dict[str, Any]]:
|
|
439
|
+
"""Apply reaction rules to each substrate in the batch.
|
|
440
|
+
|
|
441
|
+
:param rules: Iterable of rule graphs or SMILES strings.
|
|
442
|
+
:type rules: iterable
|
|
443
|
+
:param invert: Whether to apply rules in reverse direction.
|
|
444
|
+
:type invert: bool
|
|
445
|
+
:return: A list of dicts, each with:
|
|
446
|
+
- "out": list of product SMARTS/reaction SMILES
|
|
447
|
+
- "count": number of outputs
|
|
448
|
+
:rtype: list of dict
|
|
449
|
+
"""
|
|
450
|
+
rule_graphs = self._ensure_graph_rules(rules)
|
|
451
|
+
direction = "bw" if invert else "fw"
|
|
452
|
+
|
|
453
|
+
def worker(entry: Union[str, Dict[str, Any]]) -> Dict[str, Any]:
|
|
454
|
+
g = self._to_graph(entry)
|
|
455
|
+
filtered = (
|
|
456
|
+
RuleFilter(g, rule_graphs, invert=invert, engine=self._pre_filter).new_rules # type: ignore[arg-type]
|
|
457
|
+
if self._pre_filter
|
|
458
|
+
else rule_graphs
|
|
459
|
+
)
|
|
460
|
+
out = self._apply_bulk(g, filtered, invert)
|
|
461
|
+
return {f"{self._engine}_{direction}": out, "count": len(out)}
|
|
462
|
+
|
|
463
|
+
if self._entry_jobs == 1:
|
|
464
|
+
return [worker(e) for e in self._data]
|
|
465
|
+
|
|
466
|
+
return Parallel(
|
|
467
|
+
n_jobs=self._entry_jobs,
|
|
468
|
+
backend="loky",
|
|
469
|
+
prefer="processes",
|
|
470
|
+
)(delayed(worker)(e) for e in self._data)
|
|
471
|
+
|
|
472
|
+
# ------------------------------------------------------------------
|
|
473
|
+
# Additional helpers
|
|
474
|
+
# ------------------------------------------------------------------
|
|
475
|
+
|
|
476
|
+
def _to_graph(self, entry: Union[str, Dict[str, Any]]) -> nx.Graph:
|
|
477
|
+
"""Convert a SMILES string or dict entry into a networkx Graph.
|
|
478
|
+
|
|
479
|
+
:param entry: SMILES or dict holding SMILES under host_key.
|
|
480
|
+
:type entry: str or dict
|
|
481
|
+
:return: Molecule graph.
|
|
482
|
+
:rtype: networkx.Graph
|
|
483
|
+
:raises KeyError: if host_key is missing for dict.
|
|
484
|
+
:raises TypeError: if entry is not str or dict.
|
|
485
|
+
:raises ValueError: if SMILES conversion fails.
|
|
486
|
+
"""
|
|
487
|
+
if isinstance(entry, dict):
|
|
488
|
+
if self._host_key is None:
|
|
489
|
+
raise KeyError("host_key missing for dict entry")
|
|
490
|
+
entry = entry[self._host_key]
|
|
491
|
+
if not isinstance(entry, str):
|
|
492
|
+
raise TypeError("Each entry must be a SMILES string or dict containing one")
|
|
493
|
+
g = smiles_to_graph(entry, drop_non_aam=False, use_index_as_atom_map=False)
|
|
494
|
+
if g is None:
|
|
495
|
+
raise ValueError(f"Invalid SMILES: {entry}")
|
|
496
|
+
return g
|
|
497
|
+
|
|
498
|
+
@staticmethod
|
|
499
|
+
def _ensure_graph_rules(rules: Iterable[Any]) -> List[nx.Graph]:
|
|
500
|
+
"""Convert an iterable of SMILES or graph rules into graph objects.
|
|
501
|
+
|
|
502
|
+
:param rules: Iterable of rule SMILES or networkx.Graph.
|
|
503
|
+
:type rules: iterable
|
|
504
|
+
:return: List of rule graphs.
|
|
505
|
+
:rtype: list of networkx.Graph
|
|
506
|
+
:raises ValueError: if a rule SMILES is invalid.
|
|
507
|
+
:raises TypeError: for unsupported rule types.
|
|
508
|
+
"""
|
|
509
|
+
out: List[nx.Graph] = []
|
|
510
|
+
for r in rules:
|
|
511
|
+
if isinstance(r, str):
|
|
512
|
+
g = rsmi_to_its(r, core=True)
|
|
513
|
+
if g is None:
|
|
514
|
+
raise ValueError(f"Invalid rule SMILES: {r}")
|
|
515
|
+
out.append(g)
|
|
516
|
+
elif isinstance(r, nx.Graph):
|
|
517
|
+
out.append(r)
|
|
518
|
+
else:
|
|
519
|
+
raise TypeError("Rules must be str or nx.Graph")
|
|
520
|
+
return out
|
|
521
|
+
|
|
522
|
+
def _apply_bulk(
|
|
523
|
+
self, g: nx.Graph, rules: List[nx.Graph], invert: bool
|
|
524
|
+
) -> List[str]:
|
|
525
|
+
"""Apply a list of rule graphs to a substrate graph.
|
|
526
|
+
|
|
527
|
+
:param g: Substrate graph.
|
|
528
|
+
:type g: networkx.Graph
|
|
529
|
+
:param rules: List of rule graphs.
|
|
530
|
+
:type rules: list of networkx.Graph
|
|
531
|
+
:param invert: Direction flag.
|
|
532
|
+
:type invert: bool
|
|
533
|
+
:return: Flattened list of reaction outputs.
|
|
534
|
+
:rtype: list of str
|
|
535
|
+
"""
|
|
536
|
+
jobs = 1
|
|
537
|
+
if (
|
|
538
|
+
self._parallel_rules
|
|
539
|
+
and self._rule_jobs > 1
|
|
540
|
+
and (self._allow_nested or self._entry_jobs == 1)
|
|
541
|
+
):
|
|
542
|
+
jobs = self._rule_jobs
|
|
543
|
+
if jobs == 1:
|
|
544
|
+
nested = [self._apply_rule(g, r, invert) for r in rules]
|
|
545
|
+
else:
|
|
546
|
+
nested = Parallel(n_jobs=jobs, backend="loky", prefer="processes")(
|
|
547
|
+
delayed(self._apply_rule)(g, r, invert) for r in rules
|
|
548
|
+
)
|
|
549
|
+
flat = [x for sub in nested for x in sub]
|
|
550
|
+
return _dedupe(flat) if self._dedupe else flat
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Iterable, List, Optional
|
|
3
|
+
from .batch import BatchReactor
|
|
4
|
+
|
|
5
|
+
# =============================================================================
|
|
6
|
+
# Benchmark subclass
|
|
7
|
+
# =============================================================================
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Benchmark(BatchReactor): # pylint: disable=too-many-arguments
|
|
11
|
+
"""
|
|
12
|
+
Extension of BatchReactor to benchmark forward/backward application on reaction-SMILES entries.
|
|
13
|
+
|
|
14
|
+
:param data: List of dicts containing reaction SMILES under `reaction_key`.
|
|
15
|
+
:type data: list of dict
|
|
16
|
+
:param reaction_key: Key for reaction-SMILES strings (format 'reactants>>products').
|
|
17
|
+
:type reaction_key: str
|
|
18
|
+
:param pre_filter_engine: Pre-filtering engine for rules (None to skip).
|
|
19
|
+
:type pre_filter_engine: str or None
|
|
20
|
+
:param explicit_h: Use explicit hydrogens in SynReactor.
|
|
21
|
+
:type explicit_h: bool
|
|
22
|
+
:param implicit_temp: Use implicit templates in SynReactor.
|
|
23
|
+
:type implicit_temp: bool
|
|
24
|
+
:param strategy: Matching strategy for SynReactor.
|
|
25
|
+
:type strategy: str
|
|
26
|
+
:param dedupe: Deduplicate results per substrate.
|
|
27
|
+
:type dedupe: bool
|
|
28
|
+
:param entry_n_jobs: Parallel jobs for substrates.
|
|
29
|
+
:type entry_n_jobs: int
|
|
30
|
+
:param rule_n_jobs: Parallel jobs for rules per substrate.
|
|
31
|
+
:type rule_n_jobs: int
|
|
32
|
+
:param parallel_rules: Enable rule-level parallelism.
|
|
33
|
+
:type parallel_rules: bool
|
|
34
|
+
:param allow_nested: Allow nested parallelism.
|
|
35
|
+
:type allow_nested: bool
|
|
36
|
+
:param cache_enabled: Enable per-process caching.
|
|
37
|
+
:type cache_enabled: bool
|
|
38
|
+
:param cache_maxsize: Max cache entries before eviction.
|
|
39
|
+
:type cache_maxsize: int
|
|
40
|
+
:param logger: Optional custom logger.
|
|
41
|
+
:type logger: logging.Logger or None
|
|
42
|
+
:raises ValueError: If reaction_key entry malformed or SMILES invalid.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
data: List[Dict[str, Any]],
|
|
48
|
+
reaction_key: str = "reactions",
|
|
49
|
+
*,
|
|
50
|
+
pre_filter_engine: Optional[str] = None,
|
|
51
|
+
explicit_h: bool = True,
|
|
52
|
+
implicit_temp: bool = False,
|
|
53
|
+
strategy: str = "bt",
|
|
54
|
+
dedupe: bool = True,
|
|
55
|
+
entry_n_jobs: int = 1,
|
|
56
|
+
rule_n_jobs: int = 1,
|
|
57
|
+
parallel_rules: bool = False,
|
|
58
|
+
allow_nested: bool = False,
|
|
59
|
+
cache_enabled: bool = True,
|
|
60
|
+
cache_maxsize: int = 32768,
|
|
61
|
+
logger: Optional[logging.Logger] = None,
|
|
62
|
+
enable_logging: bool = True,
|
|
63
|
+
) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Initialize Benchmark with reaction entries.
|
|
66
|
+
|
|
67
|
+
Splits each reaction-SMILES into reactant 'r' and product 'p'.
|
|
68
|
+
All other parameters mirror BatchReactor (host_key set to 'r').
|
|
69
|
+
"""
|
|
70
|
+
data_prepped = self._get_host(data, reaction_key)
|
|
71
|
+
super().__init__(
|
|
72
|
+
data_prepped,
|
|
73
|
+
host_key="r",
|
|
74
|
+
pre_filter_engine=pre_filter_engine,
|
|
75
|
+
explicit_h=explicit_h,
|
|
76
|
+
implicit_temp=implicit_temp,
|
|
77
|
+
strategy=strategy,
|
|
78
|
+
dedupe=dedupe,
|
|
79
|
+
entry_n_jobs=entry_n_jobs,
|
|
80
|
+
rule_n_jobs=rule_n_jobs,
|
|
81
|
+
parallel_rules=parallel_rules,
|
|
82
|
+
allow_nested=allow_nested,
|
|
83
|
+
cache_enabled=cache_enabled,
|
|
84
|
+
cache_maxsize=cache_maxsize,
|
|
85
|
+
logger=logger,
|
|
86
|
+
enable_logging=enable_logging,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def _get_host(
|
|
91
|
+
data: List[Dict[str, Any]], reaction_key: str = "reactions"
|
|
92
|
+
) -> List[Dict[str, Any]]:
|
|
93
|
+
"""Populate 'r' and 'p' SMILES fields for each dict entry.
|
|
94
|
+
|
|
95
|
+
:param data: List of dict entries.
|
|
96
|
+
:type data: list of dict
|
|
97
|
+
:param reaction_key: Key for reaction-SMILES string.
|
|
98
|
+
:type reaction_key: str
|
|
99
|
+
:return: Same list with 'r' and 'p' keys added.
|
|
100
|
+
:rtype: list of dict
|
|
101
|
+
:raises ValueError: If any string lacks '>>'.
|
|
102
|
+
"""
|
|
103
|
+
for entry in data:
|
|
104
|
+
rxn = entry.get(reaction_key)
|
|
105
|
+
if not isinstance(rxn, str) or ">>" not in rxn:
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"Invalid reaction string for key '{reaction_key}': {rxn!r}"
|
|
108
|
+
)
|
|
109
|
+
r, p = rxn.split(">>", 1)
|
|
110
|
+
entry["r"], entry["p"] = r, p
|
|
111
|
+
return data
|
|
112
|
+
|
|
113
|
+
def fit(
|
|
114
|
+
self,
|
|
115
|
+
rules: Iterable[Any],
|
|
116
|
+
) -> List[Dict[str, Any]]:
|
|
117
|
+
"""Perform forward (invert=False) on 'r' and backward (invert=True) on 'p'.
|
|
118
|
+
|
|
119
|
+
:param rules: Iterable of rule graphs or SMILES.
|
|
120
|
+
:type rules: iterable
|
|
121
|
+
:return: List of dicts each with keys 'fw','bw','fw_count','bw_count'.
|
|
122
|
+
:rtype: list of dict
|
|
123
|
+
"""
|
|
124
|
+
fw_out = super().fit(rules, invert=False)
|
|
125
|
+
self._host_key = "p"
|
|
126
|
+
bw_out = super().fit(rules, invert=True)
|
|
127
|
+
self._host_key = "r"
|
|
128
|
+
|
|
129
|
+
for entry, fw, bw in zip(self._data, fw_out, bw_out):
|
|
130
|
+
entry["fw"] = fw.get(f"{self._engine}_fw", fw.get("out", fw))
|
|
131
|
+
entry["fw_count"] = fw.get("count", len(entry["fw"]))
|
|
132
|
+
entry["bw"] = bw.get(f"{self._engine}_bw", bw.get("out", bw))
|
|
133
|
+
entry["bw_count"] = bw.get("count", len(entry["bw"]))
|
|
134
|
+
return self._data
|
|
135
|
+
|
|
136
|
+
def describe(self) -> str:
|
|
137
|
+
"""Return detailed configuration for Benchmark, including reaction_key.
|
|
138
|
+
|
|
139
|
+
:return: Multi-line summary.
|
|
140
|
+
:rtype: str
|
|
141
|
+
"""
|
|
142
|
+
base = super().describe().splitlines()
|
|
143
|
+
return "".join(base + ["Benchmark host_key: 'r' (product host: 'p')"])
|