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,532 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
from typing import Dict, FrozenSet, List, Optional, Set, Tuple, Union
|
|
5
|
+
|
|
6
|
+
from PIL import Image, ImageDraw
|
|
7
|
+
from rdkit import Chem
|
|
8
|
+
from rdkit.Chem import AllChem, rdChemReactions, rdmolfiles
|
|
9
|
+
from rdkit.Chem.Draw import rdMolDraw2D
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RXNVis:
|
|
13
|
+
"""Visualize molecules and reactions as PIL images.
|
|
14
|
+
|
|
15
|
+
When ``highlight_reaction_center=True`` and the input is an atom-mapped
|
|
16
|
+
reaction SMILES, each molecule is drawn individually so that changed atoms
|
|
17
|
+
and bonds can be color-coded:
|
|
18
|
+
|
|
19
|
+
* **Broken bonds** (present in reactants only) — ``rc_broken_color``
|
|
20
|
+
* **Formed bonds** (present in products only) — ``rc_formed_color``
|
|
21
|
+
* **Order-changed bonds** (present on both sides with different order) — ``rc_atom_color``
|
|
22
|
+
* **Reaction-center atoms** (any atom involved in the above) — ``rc_atom_color``
|
|
23
|
+
|
|
24
|
+
Reactions without atom mapping fall back to the standard ``DrawReaction``
|
|
25
|
+
layout automatically.
|
|
26
|
+
|
|
27
|
+
:param width: Canvas width in pixels.
|
|
28
|
+
:type width: int
|
|
29
|
+
:param height: Canvas height in pixels.
|
|
30
|
+
:type height: int
|
|
31
|
+
:param dpi: DPI scaling factor (72 = 1×, i.e. no scaling).
|
|
32
|
+
:type dpi: int
|
|
33
|
+
:param background_colour: RGBA background color, each channel in [0, 1].
|
|
34
|
+
Defaults to opaque white.
|
|
35
|
+
:type background_colour: Optional[Tuple[float, float, float, float]]
|
|
36
|
+
:param highlight_by_reactant: Color each reactant differently in the
|
|
37
|
+
standard (non-RC) ``DrawReaction`` path.
|
|
38
|
+
:type highlight_by_reactant: bool
|
|
39
|
+
:param bond_line_width: Bond line width in points.
|
|
40
|
+
:type bond_line_width: float
|
|
41
|
+
:param atom_label_font_size: Base font size for atom labels (maps to
|
|
42
|
+
``MolDrawOptions.baseFontSize``).
|
|
43
|
+
:type atom_label_font_size: int
|
|
44
|
+
:param show_atom_map: Overlay atom-map numbers as atom labels.
|
|
45
|
+
:type show_atom_map: bool
|
|
46
|
+
:param highlight_reaction_center: When ``True``, detect and highlight the
|
|
47
|
+
reaction center in atom-mapped reaction SMILES.
|
|
48
|
+
:type highlight_reaction_center: bool
|
|
49
|
+
:param rc_atom_color: RGB color for reaction-center atoms and
|
|
50
|
+
order-changed bonds (values in [0, 1]).
|
|
51
|
+
:type rc_atom_color: Tuple[float, float, float]
|
|
52
|
+
:param rc_broken_color: RGB color for bonds broken in the reaction.
|
|
53
|
+
:type rc_broken_color: Tuple[float, float, float]
|
|
54
|
+
:param rc_formed_color: RGB color for bonds formed in the reaction.
|
|
55
|
+
:type rc_formed_color: Tuple[float, float, float]
|
|
56
|
+
|
|
57
|
+
.. code-block:: python
|
|
58
|
+
|
|
59
|
+
from synkit.Vis.reaction import RXNVis
|
|
60
|
+
|
|
61
|
+
vis = RXNVis(
|
|
62
|
+
width=1200,
|
|
63
|
+
height=400,
|
|
64
|
+
highlight_reaction_center=True,
|
|
65
|
+
show_atom_map=True,
|
|
66
|
+
)
|
|
67
|
+
rsmi = (
|
|
68
|
+
"[CH3:1][C:2](=[O:3])[O:4][CH3:5].[O:6]([H:7])[H:8]"
|
|
69
|
+
">>"
|
|
70
|
+
"[CH3:1][C:2](=[O:3])[O:6][H:7].[CH3:5][O:4][H:8]"
|
|
71
|
+
)
|
|
72
|
+
vis.save_png(rsmi, "transesterification.png")
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
width: int = 800,
|
|
78
|
+
height: int = 450,
|
|
79
|
+
dpi: int = 96,
|
|
80
|
+
background_colour: Optional[Tuple[float, float, float, float]] = None,
|
|
81
|
+
highlight_by_reactant: bool = True,
|
|
82
|
+
bond_line_width: float = 2.0,
|
|
83
|
+
atom_label_font_size: int = 12,
|
|
84
|
+
show_atom_map: bool = False,
|
|
85
|
+
highlight_reaction_center: bool = False,
|
|
86
|
+
rc_atom_color: Tuple[float, float, float] = (1.0, 0.5, 0.0),
|
|
87
|
+
rc_broken_color: Tuple[float, float, float] = (0.9, 0.2, 0.2),
|
|
88
|
+
rc_formed_color: Tuple[float, float, float] = (0.0, 0.75, 0.0),
|
|
89
|
+
) -> None:
|
|
90
|
+
self.width = width
|
|
91
|
+
self.height = height
|
|
92
|
+
self.dpi = dpi
|
|
93
|
+
self.background_colour = background_colour or (1.0, 1.0, 1.0, 1.0)
|
|
94
|
+
self.highlight_by_reactant = highlight_by_reactant
|
|
95
|
+
self.bond_line_width = bond_line_width
|
|
96
|
+
self.atom_label_font_size = atom_label_font_size
|
|
97
|
+
self.show_atom_map = show_atom_map
|
|
98
|
+
self.highlight_reaction_center = highlight_reaction_center
|
|
99
|
+
self.rc_atom_color = rc_atom_color
|
|
100
|
+
self.rc_broken_color = rc_broken_color
|
|
101
|
+
self.rc_formed_color = rc_formed_color
|
|
102
|
+
|
|
103
|
+
# ------------------------------------------------------------------
|
|
104
|
+
# Public API
|
|
105
|
+
# ------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
def render(
|
|
108
|
+
self, smiles: str, return_bytes: bool = False
|
|
109
|
+
) -> Union[Image.Image, bytes]:
|
|
110
|
+
"""Render a molecule or reaction SMILES to a PIL image or PNG bytes.
|
|
111
|
+
|
|
112
|
+
When ``highlight_reaction_center=True`` and ``smiles`` is an
|
|
113
|
+
atom-mapped reaction SMILES, the reaction center is highlighted.
|
|
114
|
+
Falls back to the standard ``DrawReaction`` layout when no atom
|
|
115
|
+
mapping is present.
|
|
116
|
+
|
|
117
|
+
:param smiles: Molecule SMILES or reaction SMILES containing ``">>"``.
|
|
118
|
+
:type smiles: str
|
|
119
|
+
:param return_bytes: If ``True``, return raw PNG bytes instead of a
|
|
120
|
+
``PIL.Image.Image``.
|
|
121
|
+
:type return_bytes: bool
|
|
122
|
+
:return: Rendered image or PNG bytes.
|
|
123
|
+
:rtype: Union[PIL.Image.Image, bytes]
|
|
124
|
+
:raises ValueError: If the input cannot be parsed.
|
|
125
|
+
"""
|
|
126
|
+
if ">>" in smiles and self.highlight_reaction_center:
|
|
127
|
+
img = self._render_rc(smiles)
|
|
128
|
+
else:
|
|
129
|
+
img = self._render_standard(smiles)
|
|
130
|
+
|
|
131
|
+
if self.dpi != 72:
|
|
132
|
+
scale = self.dpi / 72.0
|
|
133
|
+
img = img.resize(
|
|
134
|
+
(int(img.width * scale), int(img.height * scale)), Image.LANCZOS
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if return_bytes:
|
|
138
|
+
buf = io.BytesIO()
|
|
139
|
+
img.save(buf, format="PNG")
|
|
140
|
+
return buf.getvalue()
|
|
141
|
+
return img
|
|
142
|
+
|
|
143
|
+
def save_png(self, smiles: str, path: str) -> None:
|
|
144
|
+
"""Render and save as a PNG file.
|
|
145
|
+
|
|
146
|
+
:param smiles: Molecule or reaction SMILES.
|
|
147
|
+
:type smiles: str
|
|
148
|
+
:param path: Output path (should end in ``.png``).
|
|
149
|
+
:type path: str
|
|
150
|
+
"""
|
|
151
|
+
self.render(smiles, return_bytes=False).save(path, format="PNG")
|
|
152
|
+
|
|
153
|
+
def save_pdf(self, smiles: str, path: str, resolution: float = 300.0) -> None:
|
|
154
|
+
"""Render and save as a single-page PDF.
|
|
155
|
+
|
|
156
|
+
:param smiles: Molecule or reaction SMILES.
|
|
157
|
+
:type smiles: str
|
|
158
|
+
:param path: Output path (should end in ``.pdf``).
|
|
159
|
+
:type path: str
|
|
160
|
+
:param resolution: DPI metadata for the PDF.
|
|
161
|
+
:type resolution: float
|
|
162
|
+
"""
|
|
163
|
+
self.render(smiles, return_bytes=False).convert("RGB").save(
|
|
164
|
+
path, format="PDF", resolution=resolution
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# ------------------------------------------------------------------
|
|
168
|
+
# Standard (non-RC) rendering
|
|
169
|
+
# ------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
def _render_standard(self, smiles: str) -> Image.Image:
|
|
172
|
+
"""Render using RDKit's ``DrawReaction`` / ``DrawMolecule``.
|
|
173
|
+
|
|
174
|
+
:param smiles: Molecule or reaction SMILES.
|
|
175
|
+
:type smiles: str
|
|
176
|
+
:return: Rendered and cropped image.
|
|
177
|
+
:rtype: PIL.Image.Image
|
|
178
|
+
"""
|
|
179
|
+
drawer = rdMolDraw2D.MolDraw2DCairo(self.width, self.height, 0, 0)
|
|
180
|
+
opts = drawer.drawOptions()
|
|
181
|
+
opts.bondLineWidth = self.bond_line_width
|
|
182
|
+
opts.baseFontSize = self.atom_label_font_size
|
|
183
|
+
opts.setBackgroundColour(self.background_colour)
|
|
184
|
+
opts.includeAtomTags = self.show_atom_map
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
if ">>" in smiles:
|
|
188
|
+
rxn = rdChemReactions.ReactionFromSmarts(smiles, useSmiles=True)
|
|
189
|
+
rdChemReactions.PreprocessReaction(rxn)
|
|
190
|
+
|
|
191
|
+
if self.show_atom_map:
|
|
192
|
+
for mol in list(rxn.GetReactants()) + list(rxn.GetProducts()):
|
|
193
|
+
for atom in mol.GetAtoms():
|
|
194
|
+
if atom.HasProp("molAtomMapNumber"):
|
|
195
|
+
atom.SetProp(
|
|
196
|
+
"atomLabel", atom.GetProp("molAtomMapNumber")
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
drawer.DrawReaction(rxn, self.highlight_by_reactant, None, None)
|
|
200
|
+
else:
|
|
201
|
+
mol = rdmolfiles.MolFromSmiles(smiles) or rdmolfiles.MolFromSmarts(
|
|
202
|
+
smiles
|
|
203
|
+
)
|
|
204
|
+
if mol is None:
|
|
205
|
+
raise ValueError(f"Could not parse SMILES/SMARTS: {smiles!r}")
|
|
206
|
+
drawer.DrawMolecule(mol)
|
|
207
|
+
finally:
|
|
208
|
+
drawer.FinishDrawing()
|
|
209
|
+
|
|
210
|
+
img = Image.open(io.BytesIO(drawer.GetDrawingText())).convert("RGBA")
|
|
211
|
+
bbox = img.split()[-1].getbbox()
|
|
212
|
+
if bbox:
|
|
213
|
+
img = img.crop(bbox)
|
|
214
|
+
return img
|
|
215
|
+
|
|
216
|
+
# ------------------------------------------------------------------
|
|
217
|
+
# Reaction-center detection
|
|
218
|
+
# ------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _find_reaction_center(
|
|
222
|
+
rsmi: str,
|
|
223
|
+
) -> Tuple[
|
|
224
|
+
Set[int],
|
|
225
|
+
Set[FrozenSet[int]],
|
|
226
|
+
Set[FrozenSet[int]],
|
|
227
|
+
Set[FrozenSet[int]],
|
|
228
|
+
]:
|
|
229
|
+
"""Detect reaction-center atoms and bonds from a mapped reaction SMILES.
|
|
230
|
+
|
|
231
|
+
Compares the bond connectivity on the reactant and product sides. Only
|
|
232
|
+
bonds where **both** endpoints carry atom-map numbers are considered.
|
|
233
|
+
|
|
234
|
+
:param rsmi: Atom-mapped reaction SMILES containing ``">>"``.
|
|
235
|
+
:type rsmi: str
|
|
236
|
+
:return: A 4-tuple ``(changed_atom_maps, formed_bonds,
|
|
237
|
+
broken_bonds, changed_order_bonds)`` where each bond set contains
|
|
238
|
+
``frozenset({map_a, map_b})`` pairs.
|
|
239
|
+
:rtype: Tuple[Set[int], Set[FrozenSet[int]], Set[FrozenSet[int]], Set[FrozenSet[int]]]
|
|
240
|
+
"""
|
|
241
|
+
reactants_smi, products_smi = rsmi.split(">>", 1)
|
|
242
|
+
|
|
243
|
+
# Keep explicit H atoms that carry atom-map numbers (e.g. [H:7]) so
|
|
244
|
+
# proton-transfer bonds are correctly detected.
|
|
245
|
+
_h_params = Chem.SmilesParserParams()
|
|
246
|
+
_h_params.removeHs = False
|
|
247
|
+
|
|
248
|
+
def _bond_map(side_smi: str) -> Dict[FrozenSet[int], float]:
|
|
249
|
+
bonds: Dict[FrozenSet[int], float] = {}
|
|
250
|
+
for smi in side_smi.split("."):
|
|
251
|
+
smi = smi.strip()
|
|
252
|
+
if not smi:
|
|
253
|
+
continue
|
|
254
|
+
mol = Chem.MolFromSmiles(smi, _h_params)
|
|
255
|
+
if mol is None:
|
|
256
|
+
continue
|
|
257
|
+
for bond in mol.GetBonds():
|
|
258
|
+
a = bond.GetBeginAtom().GetAtomMapNum()
|
|
259
|
+
b = bond.GetEndAtom().GetAtomMapNum()
|
|
260
|
+
if a and b:
|
|
261
|
+
bonds[frozenset({a, b})] = bond.GetBondTypeAsDouble()
|
|
262
|
+
return bonds
|
|
263
|
+
|
|
264
|
+
r_bonds = _bond_map(reactants_smi)
|
|
265
|
+
p_bonds = _bond_map(products_smi)
|
|
266
|
+
|
|
267
|
+
formed: Set[FrozenSet[int]] = set()
|
|
268
|
+
broken: Set[FrozenSet[int]] = set()
|
|
269
|
+
changed_order: Set[FrozenSet[int]] = set()
|
|
270
|
+
changed_maps: Set[int] = set()
|
|
271
|
+
|
|
272
|
+
for pair in set(r_bonds) | set(p_bonds):
|
|
273
|
+
r_ord = r_bonds.get(pair)
|
|
274
|
+
p_ord = p_bonds.get(pair)
|
|
275
|
+
|
|
276
|
+
if r_ord is None:
|
|
277
|
+
formed.add(pair)
|
|
278
|
+
changed_maps.update(pair)
|
|
279
|
+
elif p_ord is None:
|
|
280
|
+
broken.add(pair)
|
|
281
|
+
changed_maps.update(pair)
|
|
282
|
+
elif abs(r_ord - p_ord) > 1e-3:
|
|
283
|
+
changed_order.add(pair)
|
|
284
|
+
changed_maps.update(pair)
|
|
285
|
+
|
|
286
|
+
return changed_maps, formed, broken, changed_order
|
|
287
|
+
|
|
288
|
+
# ------------------------------------------------------------------
|
|
289
|
+
# Per-molecule drawing with highlights
|
|
290
|
+
# ------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
def _draw_mol(
|
|
293
|
+
self,
|
|
294
|
+
mol: Chem.Mol,
|
|
295
|
+
mol_w: int,
|
|
296
|
+
mol_h: int,
|
|
297
|
+
changed_maps: Set[int],
|
|
298
|
+
formed: Set[FrozenSet[int]],
|
|
299
|
+
broken: Set[FrozenSet[int]],
|
|
300
|
+
changed_order: Set[FrozenSet[int]],
|
|
301
|
+
) -> Image.Image:
|
|
302
|
+
"""Draw one molecule with reaction-center highlights.
|
|
303
|
+
|
|
304
|
+
:param mol: RDKit molecule (2D coords are computed if absent).
|
|
305
|
+
:type mol: Chem.Mol
|
|
306
|
+
:param mol_w: Canvas width in pixels.
|
|
307
|
+
:type mol_w: int
|
|
308
|
+
:param mol_h: Canvas height in pixels.
|
|
309
|
+
:type mol_h: int
|
|
310
|
+
:param changed_maps: Atom-map numbers of reaction-center atoms.
|
|
311
|
+
:type changed_maps: Set[int]
|
|
312
|
+
:param formed: Bond pairs (atom-map frozensets) formed in the reaction.
|
|
313
|
+
:type formed: Set[FrozenSet[int]]
|
|
314
|
+
:param broken: Bond pairs broken in the reaction.
|
|
315
|
+
:type broken: Set[FrozenSet[int]]
|
|
316
|
+
:param changed_order: Bond pairs whose order changed.
|
|
317
|
+
:type changed_order: Set[FrozenSet[int]]
|
|
318
|
+
:return: Rendered molecule image.
|
|
319
|
+
:rtype: PIL.Image.Image
|
|
320
|
+
"""
|
|
321
|
+
if mol.GetNumConformers() == 0:
|
|
322
|
+
AllChem.Compute2DCoords(mol)
|
|
323
|
+
|
|
324
|
+
h_atoms: List[int] = []
|
|
325
|
+
h_atom_colors: Dict[int, Tuple[float, float, float]] = {}
|
|
326
|
+
for atom in mol.GetAtoms():
|
|
327
|
+
amap = atom.GetAtomMapNum()
|
|
328
|
+
if amap and amap in changed_maps:
|
|
329
|
+
idx = atom.GetIdx()
|
|
330
|
+
h_atoms.append(idx)
|
|
331
|
+
h_atom_colors[idx] = self.rc_atom_color
|
|
332
|
+
|
|
333
|
+
h_bonds: List[int] = []
|
|
334
|
+
h_bond_colors: Dict[int, Tuple[float, float, float]] = {}
|
|
335
|
+
for bond in mol.GetBonds():
|
|
336
|
+
a = bond.GetBeginAtom().GetAtomMapNum()
|
|
337
|
+
b = bond.GetEndAtom().GetAtomMapNum()
|
|
338
|
+
if not (a and b):
|
|
339
|
+
continue
|
|
340
|
+
pair = frozenset({a, b})
|
|
341
|
+
if pair in formed:
|
|
342
|
+
color = self.rc_formed_color
|
|
343
|
+
elif pair in broken:
|
|
344
|
+
color = self.rc_broken_color
|
|
345
|
+
elif pair in changed_order:
|
|
346
|
+
color = self.rc_atom_color
|
|
347
|
+
else:
|
|
348
|
+
continue
|
|
349
|
+
idx = bond.GetIdx()
|
|
350
|
+
h_bonds.append(idx)
|
|
351
|
+
h_bond_colors[idx] = color
|
|
352
|
+
|
|
353
|
+
drawer = rdMolDraw2D.MolDraw2DCairo(mol_w, mol_h)
|
|
354
|
+
opts = drawer.drawOptions()
|
|
355
|
+
opts.bondLineWidth = self.bond_line_width
|
|
356
|
+
opts.baseFontSize = self.atom_label_font_size
|
|
357
|
+
opts.setBackgroundColour(self.background_colour)
|
|
358
|
+
|
|
359
|
+
if self.show_atom_map:
|
|
360
|
+
for atom in mol.GetAtoms():
|
|
361
|
+
if atom.GetAtomMapNum():
|
|
362
|
+
atom.SetProp("atomLabel", str(atom.GetAtomMapNum()))
|
|
363
|
+
|
|
364
|
+
drawer.DrawMolecule(
|
|
365
|
+
mol,
|
|
366
|
+
highlightAtoms=h_atoms or None,
|
|
367
|
+
highlightAtomColors=h_atom_colors or None,
|
|
368
|
+
highlightBonds=h_bonds or None,
|
|
369
|
+
highlightBondColors=h_bond_colors or None,
|
|
370
|
+
)
|
|
371
|
+
drawer.FinishDrawing()
|
|
372
|
+
return Image.open(io.BytesIO(drawer.GetDrawingText())).convert("RGBA")
|
|
373
|
+
|
|
374
|
+
# ------------------------------------------------------------------
|
|
375
|
+
# Reaction-center rendering (compose per-molecule images)
|
|
376
|
+
# ------------------------------------------------------------------
|
|
377
|
+
|
|
378
|
+
def _render_rc(self, smiles: str) -> Image.Image:
|
|
379
|
+
"""Render an atom-mapped reaction SMILES with reaction-center highlights.
|
|
380
|
+
|
|
381
|
+
If no reaction center is detected (no atom mapping), falls back to
|
|
382
|
+
:meth:`_render_standard`.
|
|
383
|
+
|
|
384
|
+
:param smiles: Atom-mapped reaction SMILES.
|
|
385
|
+
:type smiles: str
|
|
386
|
+
:return: Composed reaction image.
|
|
387
|
+
:rtype: PIL.Image.Image
|
|
388
|
+
"""
|
|
389
|
+
reactants_smi, products_smi = smiles.split(">>", 1)
|
|
390
|
+
|
|
391
|
+
h_params = Chem.SmilesParserParams()
|
|
392
|
+
h_params.removeHs = False
|
|
393
|
+
|
|
394
|
+
def _parse_mols(side_smi: str) -> List[Chem.Mol]:
|
|
395
|
+
mols: List[Chem.Mol] = []
|
|
396
|
+
for smi in side_smi.split("."):
|
|
397
|
+
smi = smi.strip()
|
|
398
|
+
if not smi:
|
|
399
|
+
continue
|
|
400
|
+
mol = Chem.MolFromSmiles(smi, h_params)
|
|
401
|
+
if mol is not None:
|
|
402
|
+
AllChem.Compute2DCoords(mol)
|
|
403
|
+
mols.append(mol)
|
|
404
|
+
return mols
|
|
405
|
+
|
|
406
|
+
reactants = _parse_mols(reactants_smi)
|
|
407
|
+
products = _parse_mols(products_smi)
|
|
408
|
+
|
|
409
|
+
if not reactants and not products:
|
|
410
|
+
raise ValueError(f"Could not parse any molecules from: {smiles!r}")
|
|
411
|
+
|
|
412
|
+
changed_maps, formed, broken, changed_order = self._find_reaction_center(smiles)
|
|
413
|
+
|
|
414
|
+
# Fall back to standard layout when nothing is mapped.
|
|
415
|
+
if not changed_maps:
|
|
416
|
+
return self._render_standard(smiles)
|
|
417
|
+
|
|
418
|
+
# Per-molecule canvas dimensions.
|
|
419
|
+
n_mols = max(1, len(reactants) + len(products))
|
|
420
|
+
n_plus_r = max(0, len(reactants) - 1)
|
|
421
|
+
n_plus_p = max(0, len(products) - 1)
|
|
422
|
+
arrow_w = max(60, self.width // 10)
|
|
423
|
+
plus_w = max(30, self.width // 20)
|
|
424
|
+
total_sep_w = arrow_w + (n_plus_r + n_plus_p) * plus_w
|
|
425
|
+
mol_w = max(120, (self.width - total_sep_w) // n_mols)
|
|
426
|
+
mol_h = self.height
|
|
427
|
+
|
|
428
|
+
r_imgs = [
|
|
429
|
+
self._draw_mol(m, mol_w, mol_h, changed_maps, formed, broken, changed_order)
|
|
430
|
+
for m in reactants
|
|
431
|
+
]
|
|
432
|
+
p_imgs = [
|
|
433
|
+
self._draw_mol(m, mol_w, mol_h, changed_maps, formed, broken, changed_order)
|
|
434
|
+
for m in products
|
|
435
|
+
]
|
|
436
|
+
|
|
437
|
+
return self._compose_reaction_image(r_imgs, p_imgs, arrow_w, plus_w)
|
|
438
|
+
|
|
439
|
+
# ------------------------------------------------------------------
|
|
440
|
+
# Image composition helpers
|
|
441
|
+
# ------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
def _make_separator(self, kind: str, width: int, height: int) -> Image.Image:
|
|
444
|
+
"""Create a '+' or arrow separator image.
|
|
445
|
+
|
|
446
|
+
:param kind: ``"plus"`` for a '+' label, ``"arrow"`` for a drawn arrow.
|
|
447
|
+
:type kind: str
|
|
448
|
+
:param width: Image width in pixels.
|
|
449
|
+
:type width: int
|
|
450
|
+
:param height: Image height in pixels.
|
|
451
|
+
:type height: int
|
|
452
|
+
:return: Separator image.
|
|
453
|
+
:rtype: PIL.Image.Image
|
|
454
|
+
"""
|
|
455
|
+
bg = tuple(int(c * 255) for c in self.background_colour)
|
|
456
|
+
img = Image.new("RGBA", (width, height), bg)
|
|
457
|
+
draw = ImageDraw.Draw(img)
|
|
458
|
+
cx, cy = width // 2, height // 2
|
|
459
|
+
ink = (80, 80, 80, 255)
|
|
460
|
+
|
|
461
|
+
if kind == "arrow":
|
|
462
|
+
half = width * 3 // 8
|
|
463
|
+
lw = max(2, height // 40)
|
|
464
|
+
ah = max(6, height // 15)
|
|
465
|
+
draw.line([(cx - half, cy), (cx + half, cy)], fill=ink, width=lw)
|
|
466
|
+
draw.polygon(
|
|
467
|
+
[(cx + half, cy), (cx + half - ah, cy - ah), (cx + half - ah, cy + ah)],
|
|
468
|
+
fill=ink,
|
|
469
|
+
)
|
|
470
|
+
else:
|
|
471
|
+
font_size = max(16, height // 8)
|
|
472
|
+
try:
|
|
473
|
+
from PIL import ImageFont
|
|
474
|
+
|
|
475
|
+
font = ImageFont.load_default(size=font_size)
|
|
476
|
+
except TypeError:
|
|
477
|
+
from PIL import ImageFont
|
|
478
|
+
|
|
479
|
+
font = ImageFont.load_default()
|
|
480
|
+
draw.text((cx, cy), "+", fill=ink, anchor="mm", font=font)
|
|
481
|
+
|
|
482
|
+
return img
|
|
483
|
+
|
|
484
|
+
def _compose_reaction_image(
|
|
485
|
+
self,
|
|
486
|
+
r_imgs: List[Image.Image],
|
|
487
|
+
p_imgs: List[Image.Image],
|
|
488
|
+
arrow_w: int,
|
|
489
|
+
plus_w: int,
|
|
490
|
+
) -> Image.Image:
|
|
491
|
+
"""Horizontally compose reactant and product images.
|
|
492
|
+
|
|
493
|
+
Inserts ``'+'`` separators between molecules on each side and an
|
|
494
|
+
arrow between the two sides.
|
|
495
|
+
|
|
496
|
+
:param r_imgs: Per-reactant images.
|
|
497
|
+
:type r_imgs: List[PIL.Image.Image]
|
|
498
|
+
:param p_imgs: Per-product images.
|
|
499
|
+
:type p_imgs: List[PIL.Image.Image]
|
|
500
|
+
:param arrow_w: Width in pixels for the reaction arrow.
|
|
501
|
+
:type arrow_w: int
|
|
502
|
+
:param plus_w: Width in pixels for each '+' separator.
|
|
503
|
+
:type plus_w: int
|
|
504
|
+
:return: Final composed reaction image.
|
|
505
|
+
:rtype: PIL.Image.Image
|
|
506
|
+
"""
|
|
507
|
+
all_mol_imgs = r_imgs + p_imgs
|
|
508
|
+
h = max(img.height for img in all_mol_imgs) if all_mol_imgs else self.height
|
|
509
|
+
|
|
510
|
+
pieces: List[Image.Image] = []
|
|
511
|
+
|
|
512
|
+
for i, img in enumerate(r_imgs):
|
|
513
|
+
if i > 0:
|
|
514
|
+
pieces.append(self._make_separator("plus", plus_w, h))
|
|
515
|
+
pieces.append(img)
|
|
516
|
+
|
|
517
|
+
pieces.append(self._make_separator("arrow", arrow_w, h))
|
|
518
|
+
|
|
519
|
+
for i, img in enumerate(p_imgs):
|
|
520
|
+
if i > 0:
|
|
521
|
+
pieces.append(self._make_separator("plus", plus_w, h))
|
|
522
|
+
pieces.append(img)
|
|
523
|
+
|
|
524
|
+
total_w = sum(p.width for p in pieces)
|
|
525
|
+
bg = tuple(int(c * 255) for c in self.background_colour)
|
|
526
|
+
result = Image.new("RGBA", (total_w, h), bg)
|
|
527
|
+
x = 0
|
|
528
|
+
for piece in pieces:
|
|
529
|
+
result.paste(piece, (x, 0))
|
|
530
|
+
x += piece.width
|
|
531
|
+
|
|
532
|
+
return result
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Compatibility import for :mod:`synkit.Vis.molecule.reaction`.
|
|
2
|
+
|
|
3
|
+
New code should use :mod:`synkit.Vis.molecule`. This module remains available
|
|
4
|
+
so existing imports continue to work.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from synkit.Vis.molecule import reaction as _implementation
|
|
10
|
+
|
|
11
|
+
ReactionHighlights = _implementation.ReactionHighlights
|
|
12
|
+
draw_reaction_graph = _implementation.draw_reaction_graph
|
|
13
|
+
draw_reaction_graphs = _implementation.draw_reaction_graphs
|
|
14
|
+
find_reaction_highlights = _implementation.find_reaction_highlights
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ReactionHighlights",
|
|
18
|
+
"draw_reaction_graph",
|
|
19
|
+
"draw_reaction_graphs",
|
|
20
|
+
"find_reaction_highlights",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def __getattr__(name: str) -> object:
|
|
25
|
+
"""Delegate legacy module attributes to the canonical implementation.
|
|
26
|
+
|
|
27
|
+
:param name: Attribute requested by the caller.
|
|
28
|
+
:type name: str
|
|
29
|
+
:return: Attribute from :mod:`synkit.Vis.molecule.reaction`.
|
|
30
|
+
:rtype: object
|
|
31
|
+
"""
|
|
32
|
+
return getattr(_implementation, name)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def __dir__() -> list[str]:
|
|
36
|
+
"""Return canonical and compatibility-module attributes.
|
|
37
|
+
|
|
38
|
+
:return: Sorted attribute names.
|
|
39
|
+
:rtype: list[str]
|
|
40
|
+
"""
|
|
41
|
+
return sorted(set(globals()) | set(dir(_implementation)))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Chemical-space visualization namespace.
|
|
2
|
+
|
|
3
|
+
This namespace contains chemical-space scatter plotting and embedding helpers.
|
|
4
|
+
|
|
5
|
+
.. rubric:: Example
|
|
6
|
+
|
|
7
|
+
.. code-block:: python
|
|
8
|
+
|
|
9
|
+
from synkit.Vis.space import Embedding, scatter_plot
|
|
10
|
+
|
|
11
|
+
embedding = Embedding("./cachedir")
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from synkit.Vis.space.chemical import adjust_legend_handles, scatter_plot
|
|
15
|
+
from synkit.Vis.space.embedding import Embedding
|
|
16
|
+
|
|
17
|
+
__all__ = ["Embedding", "scatter_plot", "adjust_legend_handles"]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import matplotlib.patches as mpatches
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def scatter_plot(
|
|
7
|
+
data_train,
|
|
8
|
+
data_test,
|
|
9
|
+
size_train=10,
|
|
10
|
+
size_test=10,
|
|
11
|
+
title=None,
|
|
12
|
+
ax=None,
|
|
13
|
+
xlabel="Coordinate 1",
|
|
14
|
+
ylabel="Coordinate 2",
|
|
15
|
+
):
|
|
16
|
+
# Check if data is empty
|
|
17
|
+
if data_train.empty or data_test.empty:
|
|
18
|
+
raise ValueError("Input data frames cannot be empty.")
|
|
19
|
+
|
|
20
|
+
# Check for necessary columns
|
|
21
|
+
if data_train.columns.size < 3 or data_test.columns.size < 3:
|
|
22
|
+
raise ValueError("Data frames must have at least three columns.")
|
|
23
|
+
|
|
24
|
+
# Adding 'Type' column to differentiate between train and test data
|
|
25
|
+
data_train["Type"] = "Train"
|
|
26
|
+
data_test["Type"] = "Test"
|
|
27
|
+
|
|
28
|
+
# Combine the datasets
|
|
29
|
+
data_combined = pd.concat([data_train, data_test])
|
|
30
|
+
|
|
31
|
+
# If no axes object is passed, create one
|
|
32
|
+
if ax is None:
|
|
33
|
+
fig, ax = plt.subplots(figsize=(12, 8))
|
|
34
|
+
|
|
35
|
+
# Define a more distinct color palette
|
|
36
|
+
pastel_palette = {
|
|
37
|
+
"Train": "deepskyblue",
|
|
38
|
+
"Test": "magenta",
|
|
39
|
+
} # Using deepskyblue and magenta for better distinction
|
|
40
|
+
|
|
41
|
+
# Create scatter plots with specified sizes
|
|
42
|
+
for dtype, color in pastel_palette.items():
|
|
43
|
+
subset = data_combined[data_combined["Type"] == dtype]
|
|
44
|
+
ax.scatter(
|
|
45
|
+
subset[subset.columns[1]],
|
|
46
|
+
subset[subset.columns[2]],
|
|
47
|
+
color=color,
|
|
48
|
+
label=dtype,
|
|
49
|
+
s=size_train if dtype == "Train" else size_test,
|
|
50
|
+
alpha=0.1,
|
|
51
|
+
edgecolor="none",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Set the title if provided
|
|
55
|
+
if title:
|
|
56
|
+
ax.set_title(rf"{title}", fontsize=24, fontweight="bold")
|
|
57
|
+
|
|
58
|
+
# Set labels
|
|
59
|
+
ax.set_xlabel(xlabel, fontsize=18)
|
|
60
|
+
ax.set_ylabel(ylabel, fontsize=18)
|
|
61
|
+
|
|
62
|
+
# Enhance grid and layout
|
|
63
|
+
ax.grid(True, which="both", linestyle="--", linewidth=0.5)
|
|
64
|
+
ax.set_axisbelow(True)
|
|
65
|
+
|
|
66
|
+
# Get legend handles and labels for external usage
|
|
67
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
68
|
+
|
|
69
|
+
# Return the axes, handles, and labels for further customization outside the function
|
|
70
|
+
return ax, handles, labels
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Define a function that modifies the legend handles to full opacity for better visibility in the legend
|
|
74
|
+
def adjust_legend_handles(handles, colors):
|
|
75
|
+
new_handles = []
|
|
76
|
+
for handle, color in zip(handles, colors):
|
|
77
|
+
# Create a new handle with the same properties but with full alpha for the legend
|
|
78
|
+
new_handle = mpatches.Patch(color=color, label=handle.get_label())
|
|
79
|
+
new_handles.append(new_handle)
|
|
80
|
+
return new_handles
|