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
synkit/IO/data_io.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import pickle
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy import ndarray
|
|
6
|
+
from joblib import dump, load
|
|
7
|
+
from typing import List, Dict, Any, Generator, Optional
|
|
8
|
+
from synkit.IO.debug import setup_logging
|
|
9
|
+
|
|
10
|
+
logger = setup_logging()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def save_database(database: List[Dict], pathname: str = "./Data/database.json") -> None:
|
|
14
|
+
"""Save a database (a list of dictionaries) to a JSON file.
|
|
15
|
+
|
|
16
|
+
:param database: The database to be saved.
|
|
17
|
+
:type database: list[dict]
|
|
18
|
+
:param pathname: The path where the database will be saved. Defaults
|
|
19
|
+
to './Data/database.json'.
|
|
20
|
+
:type pathname: str
|
|
21
|
+
:raises TypeError: If the database is not a list of dictionaries.
|
|
22
|
+
:raises ValueError: If there is an error writing the file.
|
|
23
|
+
"""
|
|
24
|
+
if not all(isinstance(item, dict) for item in database):
|
|
25
|
+
raise TypeError("Database should be a list of dictionaries.")
|
|
26
|
+
try:
|
|
27
|
+
with open(pathname, "w") as f:
|
|
28
|
+
json.dump(database, f)
|
|
29
|
+
except IOError as e:
|
|
30
|
+
raise ValueError(f"Error writing to file {pathname}: {e}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_database(pathname: str = "./Data/database.json") -> List[Dict]:
|
|
34
|
+
"""Load a database (a list of dictionaries) from a JSON file.
|
|
35
|
+
|
|
36
|
+
:param pathname: The path from where the database will be loaded.
|
|
37
|
+
Defaults to './Data/database.json'.
|
|
38
|
+
:type pathname: str
|
|
39
|
+
:return: The loaded database.
|
|
40
|
+
:rtype: list[dict]
|
|
41
|
+
:raises ValueError: If there is an error reading the file.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
with open(pathname, "r") as f:
|
|
45
|
+
database = json.load(f)
|
|
46
|
+
return database
|
|
47
|
+
except IOError as e:
|
|
48
|
+
raise ValueError(f"Error reading file {pathname}: {e}")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def save_to_pickle(data: List[Dict[str, Any]], filename: str) -> None:
|
|
52
|
+
"""Save a list of dictionaries to a pickle file.
|
|
53
|
+
|
|
54
|
+
:param data: A list of dictionaries to be saved.
|
|
55
|
+
:type data: list[dict]
|
|
56
|
+
:param filename: The name of the file where the data will be saved.
|
|
57
|
+
:type filename: str
|
|
58
|
+
"""
|
|
59
|
+
with open(filename, "wb") as file:
|
|
60
|
+
pickle.dump(data, file)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def load_from_pickle(filename: str) -> List[Any]:
|
|
64
|
+
"""Load data from a pickle file.
|
|
65
|
+
|
|
66
|
+
:param filename: The name of the pickle file to load data from.
|
|
67
|
+
:type filename: str
|
|
68
|
+
:return: The data loaded from the pickle file.
|
|
69
|
+
:rtype: list
|
|
70
|
+
"""
|
|
71
|
+
with open(filename, "rb") as file:
|
|
72
|
+
return pickle.load(file)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def load_gml_as_text(gml_file_path: str) -> Optional[str]:
|
|
76
|
+
"""Load the contents of a GML file as a text string.
|
|
77
|
+
|
|
78
|
+
:param gml_file_path: The file path to the GML file.
|
|
79
|
+
:type gml_file_path: str
|
|
80
|
+
:return: The text content of the GML file, or None if the file does
|
|
81
|
+
not exist or an error occurs.
|
|
82
|
+
:rtype: str or None
|
|
83
|
+
"""
|
|
84
|
+
try:
|
|
85
|
+
with open(gml_file_path, "r") as file:
|
|
86
|
+
return file.read()
|
|
87
|
+
except FileNotFoundError:
|
|
88
|
+
logger.error(f"File not found: {gml_file_path}")
|
|
89
|
+
return None
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"An error occurred while reading {gml_file_path}: {e}")
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def save_text_as_gml(gml_text: str, file_path: str) -> bool:
|
|
96
|
+
"""Save a GML text string to a file.
|
|
97
|
+
|
|
98
|
+
:param gml_text: The GML content as a text string.
|
|
99
|
+
:type gml_text: str
|
|
100
|
+
:param file_path: The file path where the GML text will be saved.
|
|
101
|
+
:type file_path: str
|
|
102
|
+
:return: True if saving was successful, False otherwise.
|
|
103
|
+
:rtype: bool
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
with open(file_path, "w") as file:
|
|
107
|
+
file.write(gml_text)
|
|
108
|
+
logger.info(f"GML text successfully saved to {file_path}")
|
|
109
|
+
return True
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error(f"An error occurred while saving the GML text: {e}")
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def save_compressed(array: ndarray, filename: str) -> None:
|
|
116
|
+
"""Saves a NumPy array in a compressed format using .npz extension.
|
|
117
|
+
|
|
118
|
+
:param array: The NumPy array to be saved.
|
|
119
|
+
:type array: numpy.ndarray
|
|
120
|
+
:param filename: The file path or name to save the array to, with a
|
|
121
|
+
'.npz' extension.
|
|
122
|
+
:type filename: str
|
|
123
|
+
"""
|
|
124
|
+
np.savez_compressed(filename, array=array)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def load_compressed(filename: str) -> ndarray:
|
|
128
|
+
"""Loads a NumPy array from a compressed .npz file.
|
|
129
|
+
|
|
130
|
+
:param filename: The path of the .npz file to load.
|
|
131
|
+
:type filename: str
|
|
132
|
+
:return: The loaded NumPy array.
|
|
133
|
+
:rtype: numpy.ndarray
|
|
134
|
+
:raises KeyError: If the .npz file does not contain an array with
|
|
135
|
+
the key 'array'.
|
|
136
|
+
"""
|
|
137
|
+
with np.load(filename) as data:
|
|
138
|
+
if "array" in data:
|
|
139
|
+
return data["array"]
|
|
140
|
+
else:
|
|
141
|
+
raise KeyError(
|
|
142
|
+
"The .npz file does not contain an array with the key 'array'."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def save_model(model: Any, filename: str) -> None:
|
|
147
|
+
"""Save a machine learning model to a file using joblib.
|
|
148
|
+
|
|
149
|
+
:param model: The machine learning model to save.
|
|
150
|
+
:type model: object
|
|
151
|
+
:param filename: The path to the file where the model will be saved.
|
|
152
|
+
:type filename: str
|
|
153
|
+
"""
|
|
154
|
+
dump(model, filename)
|
|
155
|
+
logger.info(f"Model saved successfully to {filename}")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def load_model(filename: str) -> Any:
|
|
159
|
+
"""Load a machine learning model from a file using joblib.
|
|
160
|
+
|
|
161
|
+
:param filename: The path to the file from which the model will be
|
|
162
|
+
loaded.
|
|
163
|
+
:type filename: str
|
|
164
|
+
:return: The loaded machine learning model.
|
|
165
|
+
:rtype: object
|
|
166
|
+
"""
|
|
167
|
+
model = load(filename)
|
|
168
|
+
logger.info(f"Model loaded successfully from {filename}")
|
|
169
|
+
return model
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def save_dict_to_json(data: dict, file_path: str) -> None:
|
|
173
|
+
"""Save a dictionary to a JSON file.
|
|
174
|
+
|
|
175
|
+
:param data: The dictionary to be saved.
|
|
176
|
+
:type data: dict
|
|
177
|
+
:param file_path: The path to the file where the dictionary should
|
|
178
|
+
be saved.
|
|
179
|
+
:type file_path: str
|
|
180
|
+
"""
|
|
181
|
+
with open(file_path, "w") as json_file:
|
|
182
|
+
json.dump(data, json_file, indent=4)
|
|
183
|
+
logger.info(f"Dictionary successfully saved to {file_path}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def load_dict_from_json(file_path: str) -> Optional[dict]:
|
|
187
|
+
"""Load a dictionary from a JSON file.
|
|
188
|
+
|
|
189
|
+
:param file_path: The path to the JSON file from which to load the
|
|
190
|
+
dictionary.
|
|
191
|
+
:type file_path: str
|
|
192
|
+
:return: The dictionary loaded from the JSON file, or None if an
|
|
193
|
+
error occurs.
|
|
194
|
+
:rtype: dict or None
|
|
195
|
+
"""
|
|
196
|
+
try:
|
|
197
|
+
with open(file_path, "r") as json_file:
|
|
198
|
+
data = json.load(json_file)
|
|
199
|
+
logger.info(f"Dictionary successfully loaded from {file_path}")
|
|
200
|
+
return data
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.error(e)
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def load_from_pickle_generator(file_path: str) -> Generator[Any, None, None]:
|
|
207
|
+
"""A generator that yields items from a pickle file where each pickle load
|
|
208
|
+
returns a list of dictionaries.
|
|
209
|
+
|
|
210
|
+
:param file_path: The path to the pickle file to load.
|
|
211
|
+
:type file_path: str
|
|
212
|
+
:yield: A single item from the list of dictionaries stored in the
|
|
213
|
+
pickle file.
|
|
214
|
+
:rtype: Any
|
|
215
|
+
"""
|
|
216
|
+
with open(file_path, "rb") as file:
|
|
217
|
+
while True:
|
|
218
|
+
try:
|
|
219
|
+
batch_items = pickle.load(file)
|
|
220
|
+
for item in batch_items:
|
|
221
|
+
yield item
|
|
222
|
+
except EOFError:
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def collect_data(num_batches: int, temp_dir: str, file_template: str) -> List[Any]:
|
|
227
|
+
"""Collects and aggregates data from multiple pickle files into a single
|
|
228
|
+
list.
|
|
229
|
+
|
|
230
|
+
:param num_batches: The number of batch files to process.
|
|
231
|
+
:type num_batches: int
|
|
232
|
+
:param temp_dir: The directory where the batch files are stored.
|
|
233
|
+
:type temp_dir: str
|
|
234
|
+
:param file_template: The template string for batch file names,
|
|
235
|
+
expecting an integer formatter.
|
|
236
|
+
:type file_template: str
|
|
237
|
+
:return: A list of aggregated data items from all batch files.
|
|
238
|
+
:rtype: list
|
|
239
|
+
"""
|
|
240
|
+
collected_data: List[Any] = []
|
|
241
|
+
for i in range(num_batches):
|
|
242
|
+
file_path = os.path.join(temp_dir, file_template.format(i))
|
|
243
|
+
for item in load_from_pickle_generator(file_path):
|
|
244
|
+
collected_data.append(item)
|
|
245
|
+
return collected_data
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def save_list_to_file(data_list: list, file_path: str) -> None:
|
|
249
|
+
"""Save a list to a file in JSON format.
|
|
250
|
+
|
|
251
|
+
:param data_list: The list to save.
|
|
252
|
+
:type data_list: list
|
|
253
|
+
:param file_path: The path to the file where the list will be saved.
|
|
254
|
+
:type file_path: str
|
|
255
|
+
"""
|
|
256
|
+
with open(file_path, "w") as file:
|
|
257
|
+
json.dump(data_list, file)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def load_list_from_file(file_path: str) -> list:
|
|
261
|
+
"""Load a list from a JSON-formatted file.
|
|
262
|
+
|
|
263
|
+
:param file_path: The path to the file to read the list from.
|
|
264
|
+
:type file_path: str
|
|
265
|
+
:return: The list loaded from the file.
|
|
266
|
+
:rtype: list
|
|
267
|
+
"""
|
|
268
|
+
with open(file_path, "r") as file:
|
|
269
|
+
return json.load(file)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from typing import List, Dict, Any, Optional
|
|
2
|
+
import joblib
|
|
3
|
+
from tqdm import tqdm as _tqdm_type
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TqdmJoblib:
|
|
7
|
+
"""
|
|
8
|
+
Context manager to integrate a tqdm progress bar with joblib's Parallel.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
with TqdmJoblib(tqdm(total=100)) as t:
|
|
12
|
+
Parallel(...)(delayed(...)(...) for ...)
|
|
13
|
+
|
|
14
|
+
:param tqdm_obj: an instantiated tqdm progress bar object (e.g. `tqdm(total=...)`).
|
|
15
|
+
:type tqdm_obj: tqdm.tqdm
|
|
16
|
+
:param max_batch_size: maximum sensible batch size to accept from joblib's
|
|
17
|
+
callback; anything larger will be treated as 1 to avoid
|
|
18
|
+
corrupting the bar.
|
|
19
|
+
:type max_batch_size: int, optional
|
|
20
|
+
:param allow_overshoot: if False (default) cap updates so the bar never
|
|
21
|
+
exceeds `tqdm_obj.total`. If True, allow normal updates.
|
|
22
|
+
:type allow_overshoot: bool, optional
|
|
23
|
+
:param silent_close_errors: swallow exceptions raised when closing the tqdm
|
|
24
|
+
object on exit (default True).
|
|
25
|
+
:type silent_close_errors: bool, optional
|
|
26
|
+
:raises AttributeError: if joblib has no `parallel.BatchCompletionCallBack`.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
tqdm_obj: _tqdm_type,
|
|
32
|
+
max_batch_size: int = 10**6,
|
|
33
|
+
allow_overshoot: bool = False,
|
|
34
|
+
silent_close_errors: bool = True,
|
|
35
|
+
) -> None:
|
|
36
|
+
self.tqdm_obj = tqdm_obj
|
|
37
|
+
self.max_batch_size = int(max_batch_size)
|
|
38
|
+
self.allow_overshoot = bool(allow_overshoot)
|
|
39
|
+
self.silent_close_errors = bool(silent_close_errors)
|
|
40
|
+
|
|
41
|
+
self._orig: Optional[Any] = None
|
|
42
|
+
self._patched = False
|
|
43
|
+
|
|
44
|
+
def __enter__(self):
|
|
45
|
+
orig_cb = getattr(joblib.parallel, "BatchCompletionCallBack", None)
|
|
46
|
+
if orig_cb is None:
|
|
47
|
+
raise AttributeError("joblib.parallel.BatchCompletionCallBack not found")
|
|
48
|
+
|
|
49
|
+
self._orig = orig_cb
|
|
50
|
+
|
|
51
|
+
def _callback(*args, **kwargs):
|
|
52
|
+
try:
|
|
53
|
+
n = int(args[0])
|
|
54
|
+
if n <= 0 or n > self.max_batch_size:
|
|
55
|
+
raise ValueError
|
|
56
|
+
except Exception:
|
|
57
|
+
n = 1
|
|
58
|
+
|
|
59
|
+
if not self.allow_overshoot:
|
|
60
|
+
remaining = max(0, (self.tqdm_obj.total or 0) - (self.tqdm_obj.n or 0))
|
|
61
|
+
inc = n if n <= remaining else remaining
|
|
62
|
+
else:
|
|
63
|
+
inc = n
|
|
64
|
+
|
|
65
|
+
if inc > 0:
|
|
66
|
+
try:
|
|
67
|
+
self.tqdm_obj.update(inc)
|
|
68
|
+
except Exception:
|
|
69
|
+
# If tqdm object's update fails for any reason, fallback to +1
|
|
70
|
+
try:
|
|
71
|
+
self.tqdm_obj.update(1)
|
|
72
|
+
except Exception:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
return self._orig(*args, **kwargs)
|
|
76
|
+
|
|
77
|
+
joblib.parallel.BatchCompletionCallBack = _callback
|
|
78
|
+
self._patched = True
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
82
|
+
if self._patched and self._orig is not None:
|
|
83
|
+
try:
|
|
84
|
+
joblib.parallel.BatchCompletionCallBack = self._orig
|
|
85
|
+
except Exception:
|
|
86
|
+
# best-effort restore; ignore to avoid masking user exceptions
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
if self.tqdm_obj is not None:
|
|
91
|
+
self.tqdm_obj.close()
|
|
92
|
+
except Exception:
|
|
93
|
+
if not self.silent_close_errors:
|
|
94
|
+
raise
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def merge_dicts(
|
|
98
|
+
list1: List[Dict[str, Any]],
|
|
99
|
+
list2: List[Dict[str, Any]],
|
|
100
|
+
key: str,
|
|
101
|
+
intersection: bool = True,
|
|
102
|
+
) -> List[Dict[str, Any]]:
|
|
103
|
+
"""Merges two lists of dictionaries based on a specified key, with an
|
|
104
|
+
option to either merge only dictionaries with matching key values
|
|
105
|
+
(intersection) or all dictionaries (union).
|
|
106
|
+
|
|
107
|
+
:param list1: The first list of dictionaries.
|
|
108
|
+
:type list1: List[Dict[str, Any]]
|
|
109
|
+
:param list2: The second list of dictionaries.
|
|
110
|
+
:type list2: List[Dict[str, Any]]
|
|
111
|
+
:param key: The key used to match and merge dictionaries from both lists.
|
|
112
|
+
:type key: str
|
|
113
|
+
:param intersection: If True, only merge dictionaries with matching key values;
|
|
114
|
+
if False, merge all dictionaries, combining those with matching key values.
|
|
115
|
+
:type intersection: bool
|
|
116
|
+
|
|
117
|
+
:return: Dictionaries merged according to the selected strategy.
|
|
118
|
+
:rtype: List[Dict[str, Any]]
|
|
119
|
+
"""
|
|
120
|
+
dict1 = {item[key]: item for item in list1}
|
|
121
|
+
dict2 = {item[key]: item for item in list2}
|
|
122
|
+
|
|
123
|
+
if intersection:
|
|
124
|
+
# Intersection of keys: only keys present in both dictionaries are merged
|
|
125
|
+
merged_list = []
|
|
126
|
+
for item1 in list1:
|
|
127
|
+
r_id = item1.get(key)
|
|
128
|
+
if r_id in dict2:
|
|
129
|
+
merged_item = {**item1, **dict2[r_id]}
|
|
130
|
+
merged_list.append(merged_item)
|
|
131
|
+
return merged_list
|
|
132
|
+
else:
|
|
133
|
+
# Union of keys: all keys from both dictionaries are merged
|
|
134
|
+
merged_dict = {}
|
|
135
|
+
all_keys = set(dict1) | set(dict2)
|
|
136
|
+
for k in all_keys:
|
|
137
|
+
if k in dict1 and k in dict2:
|
|
138
|
+
merged_dict[k] = {**dict1[k], **dict2[k]}
|
|
139
|
+
elif k in dict1:
|
|
140
|
+
merged_dict[k] = dict1[k]
|
|
141
|
+
else:
|
|
142
|
+
merged_dict[k] = dict2[k]
|
|
143
|
+
return list(merged_dict.values())
|
synkit/IO/debug.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import warnings
|
|
4
|
+
from rdkit import rdBase, RDLogger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def setup_logging(
|
|
8
|
+
log_level: str = "INFO", log_filename: str = None, task_type: str = None
|
|
9
|
+
) -> logging.Logger:
|
|
10
|
+
"""Configures logging to either the console or a file, based on provided
|
|
11
|
+
parameters.
|
|
12
|
+
|
|
13
|
+
:param log_level: Logging level to set. Defaults to 'INFO'.
|
|
14
|
+
Options: 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'.
|
|
15
|
+
:type log_level: str
|
|
16
|
+
:param log_filename: If provided, logs are written to this file. Defaults to None (logs to console).
|
|
17
|
+
:type log_filename: str or None
|
|
18
|
+
:param task_type: Logger name/namespace. Useful for distinguishing loggers in multi-task settings.
|
|
19
|
+
Defaults to None.
|
|
20
|
+
:type task_type: str or None
|
|
21
|
+
|
|
22
|
+
:return: Configured logger instance.
|
|
23
|
+
:rtype: logging.Logger
|
|
24
|
+
|
|
25
|
+
:raises ValueError: If an invalid log level is provided.
|
|
26
|
+
"""
|
|
27
|
+
log_format = "%(asctime)s - %(levelname)s - %(message)s"
|
|
28
|
+
numeric_level = getattr(logging, log_level.upper(), None)
|
|
29
|
+
|
|
30
|
+
if not isinstance(numeric_level, int):
|
|
31
|
+
raise ValueError(f"Invalid log level: {log_level}")
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(task_type)
|
|
34
|
+
logger.handlers.clear() # Efficiently remove all existing handlers
|
|
35
|
+
|
|
36
|
+
if log_filename:
|
|
37
|
+
os.makedirs(os.path.dirname(log_filename), exist_ok=True)
|
|
38
|
+
logging.basicConfig(
|
|
39
|
+
level=numeric_level, format=log_format, filename=log_filename, filemode="a"
|
|
40
|
+
)
|
|
41
|
+
else:
|
|
42
|
+
logging.basicConfig(level=numeric_level, format=log_format)
|
|
43
|
+
|
|
44
|
+
return logger
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def configure_warnings_and_logs(
|
|
48
|
+
ignore_warnings: bool = False, disable_rdkit_logs: bool = False
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Configures Python warnings and RDKit log behavior based on input flags.
|
|
51
|
+
|
|
52
|
+
:param ignore_warnings: Whether to suppress all Python warnings.
|
|
53
|
+
Default is False.
|
|
54
|
+
:type ignore_warnings: bool
|
|
55
|
+
:param disable_rdkit_logs: Whether to disable RDKit error and
|
|
56
|
+
warning logs. Default is False.
|
|
57
|
+
:type disable_rdkit_logs: bool
|
|
58
|
+
:return: None :usage: Use this function to control verbosity (e.g.
|
|
59
|
+
in production or testing), but use with caution during
|
|
60
|
+
development to avoid missing critical issues.
|
|
61
|
+
"""
|
|
62
|
+
if ignore_warnings:
|
|
63
|
+
warnings.filterwarnings("ignore")
|
|
64
|
+
else:
|
|
65
|
+
warnings.resetwarnings()
|
|
66
|
+
|
|
67
|
+
if disable_rdkit_logs:
|
|
68
|
+
rdBase.DisableLog("rdApp.error")
|
|
69
|
+
rdBase.DisableLog("rdApp.warning")
|
|
70
|
+
RDLogger.DisableLog("rdApp.warning")
|
|
71
|
+
else:
|
|
72
|
+
rdBase.EnableLog("rdApp.error")
|
|
73
|
+
rdBase.EnableLog("rdApp.warning")
|
synkit/IO/gml_to_nx.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import networkx as nx
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from synkit.Graph.ITS.its_construction import ITSConstruction
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GMLToNX:
|
|
8
|
+
"""Parses GML-like text and transforms it into three NetworkX graphs
|
|
9
|
+
representing the left, right, and context graphs of a chemical reaction
|
|
10
|
+
step.
|
|
11
|
+
|
|
12
|
+
:param gml_text: The GML-like text to parse.
|
|
13
|
+
:type gml_text: str
|
|
14
|
+
:ivar graphs: A dictionary containing 'left', 'right', and 'context'
|
|
15
|
+
NetworkX graphs.
|
|
16
|
+
:vartype graphs: dict[str, nx.Graph]
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, gml_text: str):
|
|
20
|
+
"""Initializes a GMLToNX object that can parse GML-like text into
|
|
21
|
+
separate NetworkX graphs representing different stages or components of
|
|
22
|
+
a chemical reaction.
|
|
23
|
+
|
|
24
|
+
:param gml_text: The GML-like text to be parsed.
|
|
25
|
+
:type gml_text: str
|
|
26
|
+
"""
|
|
27
|
+
self.gml_text = gml_text
|
|
28
|
+
self.graphs = {"left": nx.Graph(), "context": nx.Graph(), "right": nx.Graph()}
|
|
29
|
+
|
|
30
|
+
def _parse_element(self, line: str, current_section: str):
|
|
31
|
+
"""Parses a line of GML-like text to extract node or edge data and adds
|
|
32
|
+
it to the current section's graph.
|
|
33
|
+
|
|
34
|
+
:param line: A single line of GML-like text.
|
|
35
|
+
:type line: str
|
|
36
|
+
:param current_section: Which section ('left', 'right',
|
|
37
|
+
'context') to add the node/edge to.
|
|
38
|
+
:type current_section: str
|
|
39
|
+
"""
|
|
40
|
+
label_to_order = {"-": 1, ":": 1.5, "=": 2, "#": 3}
|
|
41
|
+
tokens = line.split()
|
|
42
|
+
|
|
43
|
+
if "node" in line:
|
|
44
|
+
node_id = int(tokens[tokens.index("id") + 1])
|
|
45
|
+
label = tokens[tokens.index("label") + 1].strip('"')
|
|
46
|
+
element, charge = self._extract_element_and_charge(label)
|
|
47
|
+
node_attributes = {
|
|
48
|
+
"element": element,
|
|
49
|
+
"charge": charge,
|
|
50
|
+
"atom_map": node_id,
|
|
51
|
+
"hcount": 0,
|
|
52
|
+
}
|
|
53
|
+
self.graphs[current_section].add_node(node_id, **node_attributes)
|
|
54
|
+
|
|
55
|
+
elif "edge" in line:
|
|
56
|
+
source = int(tokens[tokens.index("source") + 1])
|
|
57
|
+
target = int(tokens[tokens.index("target") + 1])
|
|
58
|
+
label = tokens[tokens.index("label") + 1].strip('"')
|
|
59
|
+
order = label_to_order.get(label, 0)
|
|
60
|
+
self.graphs[current_section].add_edge(source, target, order=order)
|
|
61
|
+
|
|
62
|
+
def _extract_element_and_charge(self, label: str) -> Tuple[str, int]:
|
|
63
|
+
"""Extracts the chemical element and its charge from a node label.
|
|
64
|
+
|
|
65
|
+
:param label: The label string from a GML node (e.g., 'N+',
|
|
66
|
+
'O2-', etc.).
|
|
67
|
+
:type label: str
|
|
68
|
+
:return: A tuple of (element symbol, formal charge).
|
|
69
|
+
:rtype: tuple[str, int]
|
|
70
|
+
"""
|
|
71
|
+
match = re.match(r"([A-Za-z*]+)(\d+)?([+-])?$", label)
|
|
72
|
+
if not match:
|
|
73
|
+
return ("X", 0)
|
|
74
|
+
element = match.group(1)
|
|
75
|
+
num = match.group(2)
|
|
76
|
+
sign = match.group(3)
|
|
77
|
+
charge = 0
|
|
78
|
+
if sign:
|
|
79
|
+
charge_val = int(num) if num else 1
|
|
80
|
+
charge = charge_val if sign == "+" else -charge_val
|
|
81
|
+
return element, charge
|
|
82
|
+
|
|
83
|
+
def _synchronize_nodes_and_edges(self):
|
|
84
|
+
"""Ensures that all nodes and edges in 'context' appear in both 'left'
|
|
85
|
+
and 'right'. We do not remove edges from left or right if they are not
|
|
86
|
+
in context. We only add missing context nodes and edges to left and
|
|
87
|
+
right.
|
|
88
|
+
|
|
89
|
+
:return: None
|
|
90
|
+
"""
|
|
91
|
+
# Add missing context nodes to left and right
|
|
92
|
+
for node, ndata in self.graphs["context"].nodes(data=True):
|
|
93
|
+
if node not in self.graphs["left"]:
|
|
94
|
+
self.graphs["left"].add_node(node, **ndata)
|
|
95
|
+
else:
|
|
96
|
+
# Merge attributes if node already exists in left
|
|
97
|
+
for k, v in ndata.items():
|
|
98
|
+
self.graphs["left"].nodes[node][k] = v
|
|
99
|
+
|
|
100
|
+
if node not in self.graphs["right"]:
|
|
101
|
+
self.graphs["right"].add_node(node, **ndata)
|
|
102
|
+
else:
|
|
103
|
+
# Merge attributes if node already exists in right
|
|
104
|
+
for k, v in ndata.items():
|
|
105
|
+
self.graphs["right"].nodes[node][k] = v
|
|
106
|
+
|
|
107
|
+
# Add missing context edges to left and right
|
|
108
|
+
for s, t, edata in self.graphs["context"].edges(data=True):
|
|
109
|
+
if not self.graphs["left"].has_edge(s, t):
|
|
110
|
+
self.graphs["left"].add_edge(s, t, **edata)
|
|
111
|
+
if not self.graphs["right"].has_edge(s, t):
|
|
112
|
+
self.graphs["right"].add_edge(s, t, **edata)
|
|
113
|
+
|
|
114
|
+
def transform(self) -> Tuple[nx.Graph, nx.Graph, nx.Graph]:
|
|
115
|
+
"""Transforms the GML-like text into three NetworkX graphs: left, right, and context.
|
|
116
|
+
|
|
117
|
+
:return: A tuple of (left_graph, right_graph, context_graph), each a NetworkX graph.
|
|
118
|
+
:rtype: tuple[nx.Graph, nx.Graph, nx.Graph]
|
|
119
|
+
"""
|
|
120
|
+
current_section = None
|
|
121
|
+
lines = self.gml_text.split("\n")
|
|
122
|
+
for line in lines:
|
|
123
|
+
line = line.strip()
|
|
124
|
+
if line.startswith("rule") or line == "]":
|
|
125
|
+
continue
|
|
126
|
+
if any(section in line for section in ["left", "context", "right"]):
|
|
127
|
+
current_section = line.split("[")[0].strip()
|
|
128
|
+
continue
|
|
129
|
+
if line.startswith("node") or line.startswith("edge"):
|
|
130
|
+
self._parse_element(line, current_section)
|
|
131
|
+
|
|
132
|
+
# Synchronize after parsing
|
|
133
|
+
self._synchronize_nodes_and_edges()
|
|
134
|
+
|
|
135
|
+
# Create the ITS graph
|
|
136
|
+
its_graph = ITSConstruction().ITSGraph(
|
|
137
|
+
self.graphs["left"], self.graphs["right"]
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Restore node attributes in ITS graph from left (or right)
|
|
141
|
+
for n in its_graph.nodes():
|
|
142
|
+
if n in self.graphs["left"].nodes:
|
|
143
|
+
for k, v in self.graphs["left"].nodes[n].items():
|
|
144
|
+
its_graph.nodes[n][k] = v
|
|
145
|
+
|
|
146
|
+
self.graphs["context"] = ITSConstruction().ITSGraph(
|
|
147
|
+
self.graphs["left"], self.graphs["right"]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return self.graphs["left"], self.graphs["right"], self.graphs["context"]
|