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
|
File without changes
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import networkx as nx
|
|
3
|
+
from collections import Counter
|
|
4
|
+
from hashlib import blake2b
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Dict, List, Optional, Any, Union
|
|
7
|
+
import numpy as np
|
|
8
|
+
from synkit.IO import rsmi_to_graph
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class WLRnxFps:
|
|
13
|
+
"""
|
|
14
|
+
Weisfeiler-Lehman Reaction Fingerprint Sketch (NetworkX).
|
|
15
|
+
|
|
16
|
+
:param radius: number of WL refinement iterations
|
|
17
|
+
:type radius: int
|
|
18
|
+
:param size: bit budget for the parity sketch
|
|
19
|
+
:type size: int
|
|
20
|
+
:param to_array: whether to return fingerprint as NumPy array
|
|
21
|
+
:type to_array: bool
|
|
22
|
+
|
|
23
|
+
Usage example:
|
|
24
|
+
>>> rsmi = ('COc1cc(NC(N)=S)ccc1-n1cnc(C)c1.O=C1C(Br)CCCC1c1ccc(Cl)cc1Cl'
|
|
25
|
+
>>> +'>>Br.COc1cc(Nc2nc3c(s2)CCCC3c2ccc(Cl)cc2Cl)ccc1-n1cnc(C)c1.O')
|
|
26
|
+
>>> react, prod = rsmi_to_graph(rsmi, drop_non_aam=False, use_index_as_atom_map=False)
|
|
27
|
+
>>> fps = WLRnxFps(radius=2, size=1024, to_array=False).fit(react, prod)
|
|
28
|
+
>>> bits = fps.fingerprint
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
radius: int = 2
|
|
32
|
+
size: int = 1024
|
|
33
|
+
to_array: bool = False
|
|
34
|
+
|
|
35
|
+
_tokens_R: Optional[Counter] = field(init=False, default=None)
|
|
36
|
+
_tokens_P: Optional[Counter] = field(init=False, default=None)
|
|
37
|
+
_delta: Optional[Counter] = field(init=False, default=None)
|
|
38
|
+
_support: Optional[List[int]] = field(init=False, default=None)
|
|
39
|
+
_fingerprint: Optional[Union[List[int], np.ndarray]] = field(
|
|
40
|
+
init=False, default=None
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def fit(self, react: nx.Graph, prod: nx.Graph) -> WLRnxFps:
|
|
44
|
+
"""Compute WL tokens for reactant and product graphs, then build parity sketch on Δ-support.
|
|
45
|
+
|
|
46
|
+
:param react: reactant graph with node attrs 'element','aromatic','hcount','charge'
|
|
47
|
+
:type react: nx.Graph
|
|
48
|
+
:param prod: product graph with same node/edge attrs
|
|
49
|
+
:type prod: nx.Graph
|
|
50
|
+
:return: self
|
|
51
|
+
:rtype: WLRnxFps
|
|
52
|
+
:raises ValueError: if size is not positive
|
|
53
|
+
"""
|
|
54
|
+
if self.size <= 0:
|
|
55
|
+
raise ValueError("size must be a positive integer")
|
|
56
|
+
|
|
57
|
+
def wl_tokens(G: nx.Graph) -> Counter:
|
|
58
|
+
labels: Dict[int, int] = {}
|
|
59
|
+
for n, attrs in G.nodes(data=True):
|
|
60
|
+
atom_tuple = (
|
|
61
|
+
attrs.get("element"),
|
|
62
|
+
bool(attrs.get("aromatic", False)),
|
|
63
|
+
int(attrs.get("charge", 0)),
|
|
64
|
+
int(attrs.get("hcount", 0)),
|
|
65
|
+
G.degree(n),
|
|
66
|
+
)
|
|
67
|
+
labels[n] = _h64(("init", atom_tuple))
|
|
68
|
+
cnt = Counter(labels.values())
|
|
69
|
+
for k in range(1, self.radius + 1):
|
|
70
|
+
new_labels: Dict[int, int] = {}
|
|
71
|
+
for n in G.nodes():
|
|
72
|
+
neigh = []
|
|
73
|
+
for m in G.neighbors(n):
|
|
74
|
+
bond_order = float(G.edges[n, m].get("order", 1.0))
|
|
75
|
+
neigh.append((_h64(("bond", bond_order)), labels[m]))
|
|
76
|
+
neigh.sort()
|
|
77
|
+
new_labels[n] = _h64(("wl", k, labels[n], tuple(neigh)))
|
|
78
|
+
labels = new_labels
|
|
79
|
+
cnt.update(labels.values())
|
|
80
|
+
return cnt
|
|
81
|
+
|
|
82
|
+
TR = wl_tokens(react)
|
|
83
|
+
TP = wl_tokens(prod)
|
|
84
|
+
|
|
85
|
+
Delta = Counter(TP)
|
|
86
|
+
for h, v in TR.items():
|
|
87
|
+
Delta[h] -= v
|
|
88
|
+
if Delta[h] == 0:
|
|
89
|
+
del Delta[h]
|
|
90
|
+
|
|
91
|
+
support = list(Delta.keys())
|
|
92
|
+
self._tokens_R = TR
|
|
93
|
+
self._tokens_P = TP
|
|
94
|
+
self._delta = Delta
|
|
95
|
+
self._support = support
|
|
96
|
+
|
|
97
|
+
bits = np.zeros(self.size, dtype=int) if self.to_array else [0] * self.size
|
|
98
|
+
for h in support:
|
|
99
|
+
idx = h % self.size
|
|
100
|
+
if self.to_array:
|
|
101
|
+
bits[idx] ^= 1
|
|
102
|
+
else:
|
|
103
|
+
bits[idx] = bits[idx] ^ 1
|
|
104
|
+
self._fingerprint = bits
|
|
105
|
+
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def from_rsmi(
|
|
110
|
+
cls,
|
|
111
|
+
rsmi: str,
|
|
112
|
+
radius: int = 2,
|
|
113
|
+
size: int = 1024,
|
|
114
|
+
to_array: bool = False,
|
|
115
|
+
drop_non_aam: bool = False,
|
|
116
|
+
use_index_as_atom_map: bool = False,
|
|
117
|
+
) -> WLRnxFps:
|
|
118
|
+
"""Build WLRnxFps directly from a reaction SMILES string.
|
|
119
|
+
|
|
120
|
+
:param rsmi: reaction SMILES string
|
|
121
|
+
:type rsmi: str
|
|
122
|
+
:param radius: number of WL refinement iterations
|
|
123
|
+
:type radius: int
|
|
124
|
+
:param size: bit budget for the parity sketch
|
|
125
|
+
:type size: int
|
|
126
|
+
:param to_array: return fingerprint as NumPy array if True
|
|
127
|
+
:type to_array: bool
|
|
128
|
+
:param drop_non_aam: drop atoms without atom-atom mapping
|
|
129
|
+
:type drop_non_aam: bool
|
|
130
|
+
:param use_index_as_atom_map: interpret node indices as atom map numbers
|
|
131
|
+
:type use_index_as_atom_map: bool
|
|
132
|
+
:return: fitted WLRnxFps instance
|
|
133
|
+
:rtype: WLRnxFps
|
|
134
|
+
:raises ValueError: on invalid SMILES parsing
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
react, prod = rsmi_to_graph(
|
|
138
|
+
rsmi,
|
|
139
|
+
drop_non_aam=drop_non_aam,
|
|
140
|
+
use_index_as_atom_map=use_index_as_atom_map,
|
|
141
|
+
)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
raise ValueError(f"Failed to parse rsmi: {e}")
|
|
144
|
+
|
|
145
|
+
return cls(radius=radius, size=size, to_array=to_array).fit(react, prod)
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def tokens_R(self) -> Counter:
|
|
149
|
+
""":return: WL token counts for reactant
|
|
150
|
+
:rtype: Counter
|
|
151
|
+
:raises AttributeError: if fit() has not been called
|
|
152
|
+
"""
|
|
153
|
+
if self._tokens_R is None:
|
|
154
|
+
raise AttributeError("Call fit() before accessing tokens_R")
|
|
155
|
+
return self._tokens_R
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def tokens_P(self) -> Counter:
|
|
159
|
+
""":return: WL token counts for product
|
|
160
|
+
:rtype: Counter
|
|
161
|
+
:raises AttributeError: if fit() has not been called
|
|
162
|
+
"""
|
|
163
|
+
if self._tokens_P is None:
|
|
164
|
+
raise AttributeError("Call fit() before accessing tokens_P")
|
|
165
|
+
return self._tokens_P
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def delta(self) -> Counter:
|
|
169
|
+
""":return: Signed token difference (product - reactant)
|
|
170
|
+
:rtype: Counter
|
|
171
|
+
:raises AttributeError: if fit() has not been called
|
|
172
|
+
"""
|
|
173
|
+
if self._delta is None:
|
|
174
|
+
raise AttributeError("Call fit() before accessing delta")
|
|
175
|
+
return self._delta
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def support(self) -> List[int]:
|
|
179
|
+
""":return: Tokens with non-zero delta
|
|
180
|
+
:rtype: List[int]
|
|
181
|
+
:raises AttributeError: if fit() has not been called
|
|
182
|
+
"""
|
|
183
|
+
if self._support is None:
|
|
184
|
+
raise AttributeError("Call fit() before accessing support")
|
|
185
|
+
return self._support
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def fingerprint(self) -> Union[List[int], np.ndarray]:
|
|
189
|
+
""":return: Parity sketch bit vector (0/1)
|
|
190
|
+
:rtype: Union[List[int], numpy.ndarray]
|
|
191
|
+
:raises AttributeError: if fit() has not been called
|
|
192
|
+
"""
|
|
193
|
+
if self._fingerprint is None:
|
|
194
|
+
raise AttributeError("Call fit() before accessing fingerprint")
|
|
195
|
+
return self._fingerprint
|
|
196
|
+
|
|
197
|
+
def __repr__(self) -> str:
|
|
198
|
+
support_len = len(self._support) if self._support is not None else 0
|
|
199
|
+
return (
|
|
200
|
+
f"<WLRnxFps radius={self.radius} size={self.size} "
|
|
201
|
+
f"support={support_len} to_array={self.to_array}>"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def help(self) -> None:
|
|
205
|
+
"""Print usage examples and class docstring.
|
|
206
|
+
|
|
207
|
+
:return: None
|
|
208
|
+
"""
|
|
209
|
+
print(self.__doc__)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _h64(obj: Any) -> int:
|
|
213
|
+
"""Compute a stable 64-bit hash of an object.
|
|
214
|
+
|
|
215
|
+
:param obj: any hashable representation
|
|
216
|
+
:type obj: Any
|
|
217
|
+
:return: 64-bit integer hash
|
|
218
|
+
:rtype: int
|
|
219
|
+
"""
|
|
220
|
+
h = blake2b(digest_size=8)
|
|
221
|
+
h.update(repr(obj).encode("utf-8"))
|
|
222
|
+
return int.from_bytes(h.digest(), "little")
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import networkx as nx
|
|
2
|
+
from joblib import Parallel, delayed
|
|
3
|
+
from typing import List, Dict, Any, Union
|
|
4
|
+
from collections import Counter, OrderedDict
|
|
5
|
+
from synkit.IO.debug import setup_logging
|
|
6
|
+
from synkit.Graph.Feature.graph_signature import GraphSignature
|
|
7
|
+
|
|
8
|
+
logger = setup_logging()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GraphDescriptor:
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def is_graph_empty(graph: Union[nx.Graph, dict, list, Any]) -> bool:
|
|
17
|
+
"""Determine if a graph representation is empty.
|
|
18
|
+
|
|
19
|
+
:param graph: A graph representation which can be
|
|
20
|
+
a NetworkX graph, a dictionary, a list, or an object with an 'is_empty' method.
|
|
21
|
+
:type graph: Union[nx.Graph, dict, list, Any]
|
|
22
|
+
|
|
23
|
+
:return: True if the graph is empty, False otherwise.
|
|
24
|
+
:rtype: bool
|
|
25
|
+
|
|
26
|
+
:raises TypeError: If the graph representation is not supported.
|
|
27
|
+
"""
|
|
28
|
+
if isinstance(graph, nx.Graph):
|
|
29
|
+
return graph.number_of_nodes() == 0
|
|
30
|
+
elif isinstance(graph, dict):
|
|
31
|
+
return len(graph) == 0
|
|
32
|
+
elif isinstance(graph, list):
|
|
33
|
+
return all(len(row) == 0 for row in graph)
|
|
34
|
+
elif hasattr(graph, "is_empty"):
|
|
35
|
+
return graph.is_empty()
|
|
36
|
+
else:
|
|
37
|
+
raise TypeError("Unsupported graph representation")
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def is_acyclic_graph(G: nx.Graph) -> bool:
|
|
41
|
+
"""Determines if the given graph is acyclic.
|
|
42
|
+
|
|
43
|
+
:param G: The graph to be checked.
|
|
44
|
+
:type G: nx.Graph
|
|
45
|
+
|
|
46
|
+
:return: True if the graph is acyclic, False otherwise.
|
|
47
|
+
:rtype: bool
|
|
48
|
+
"""
|
|
49
|
+
GraphDescriptor._validate_graph_input(G)
|
|
50
|
+
return nx.is_tree(G) if not GraphDescriptor.is_graph_empty(G) else False
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def is_single_cyclic_graph(G: nx.Graph) -> bool:
|
|
54
|
+
"""Determines if the given graph has exactly one cycle.
|
|
55
|
+
|
|
56
|
+
:param G: The graph to be checked.
|
|
57
|
+
:type G: nx.Graph
|
|
58
|
+
|
|
59
|
+
:return: True if the graph is single cyclic, False otherwise.
|
|
60
|
+
:rtype: bool
|
|
61
|
+
"""
|
|
62
|
+
GraphDescriptor._validate_graph_input(G)
|
|
63
|
+
if GraphDescriptor.is_graph_empty(G) or not nx.is_connected(G):
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
cycles = nx.cycle_basis(G)
|
|
67
|
+
if cycles and set(G.nodes()) == {node for cycle in cycles for node in cycle}:
|
|
68
|
+
return G.number_of_edges() == G.number_of_nodes()
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def is_complex_cyclic_graph(G: nx.Graph) -> bool:
|
|
73
|
+
"""Determines if the graph is complex cyclic with multiple cycles.
|
|
74
|
+
|
|
75
|
+
:param G: The graph to be checked.
|
|
76
|
+
:type G: nx.Graph
|
|
77
|
+
|
|
78
|
+
:return: True if the graph is complex cyclic, False otherwise.
|
|
79
|
+
:rtype: bool
|
|
80
|
+
"""
|
|
81
|
+
GraphDescriptor._validate_graph_input(G)
|
|
82
|
+
if GraphDescriptor.is_graph_empty(G) or not nx.is_connected(G):
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
cycles = nx.minimum_cycle_basis(G)
|
|
86
|
+
nodes_in_cycles = {node for cycle in cycles for node in cycle}
|
|
87
|
+
return len(cycles) > 1 and nodes_in_cycles == set(G.nodes())
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def check_graph_type(G: nx.Graph) -> str:
|
|
91
|
+
"""Classifies the graph as acyclic, single cyclic, or complex cyclic.
|
|
92
|
+
|
|
93
|
+
:param G: The graph to be checked.
|
|
94
|
+
:type G: nx.Graph
|
|
95
|
+
|
|
96
|
+
:return: The classification result.
|
|
97
|
+
:rtype: str
|
|
98
|
+
"""
|
|
99
|
+
GraphDescriptor._validate_graph_input(G)
|
|
100
|
+
if GraphDescriptor.is_graph_empty(G):
|
|
101
|
+
return "Empty Graph"
|
|
102
|
+
elif GraphDescriptor.is_acyclic_graph(G):
|
|
103
|
+
return "Acyclic"
|
|
104
|
+
elif GraphDescriptor.is_single_cyclic_graph(G):
|
|
105
|
+
return "Single Cyclic"
|
|
106
|
+
elif GraphDescriptor.is_complex_cyclic_graph(G):
|
|
107
|
+
return "Combinatorial Cyclic"
|
|
108
|
+
else:
|
|
109
|
+
return "Complex Cyclic"
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def get_cycle_member_rings(G: nx.Graph, type="minimal") -> List[int]:
|
|
113
|
+
"""Identifies all cycles in the given graph using cycle bases to ensure
|
|
114
|
+
no overlap and returns a list of the sizes of these cycles (member
|
|
115
|
+
rings), sorted in ascending order.
|
|
116
|
+
|
|
117
|
+
:param G: The NetworkX graph to be analyzed.
|
|
118
|
+
:type G: nx.Graph
|
|
119
|
+
|
|
120
|
+
:return: A sorted list of cycle sizes (member rings) found in the graph.
|
|
121
|
+
:rtype: List[int]
|
|
122
|
+
"""
|
|
123
|
+
if not isinstance(G, nx.Graph):
|
|
124
|
+
raise TypeError("Input must be a networkx Graph object.")
|
|
125
|
+
|
|
126
|
+
if type == "minimal":
|
|
127
|
+
cycles = nx.minimum_cycle_basis(G)
|
|
128
|
+
else:
|
|
129
|
+
cycles = nx.cycle_basis(G)
|
|
130
|
+
member_rings = [len(cycle) for cycle in cycles]
|
|
131
|
+
|
|
132
|
+
member_rings.sort()
|
|
133
|
+
|
|
134
|
+
return member_rings
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def get_element_count(graph: nx.Graph) -> Dict[str, int]:
|
|
138
|
+
"""Counts occurrences of each element in the graph nodes.
|
|
139
|
+
|
|
140
|
+
:param graph: A NetworkX graph with 'element' attribute in nodes.
|
|
141
|
+
:type graph: nx.Graph
|
|
142
|
+
|
|
143
|
+
:return: An ordered dictionary with element counts.
|
|
144
|
+
:rtype: Dict[str, int]
|
|
145
|
+
"""
|
|
146
|
+
element_counts = Counter(data["element"] for _, data in graph.nodes(data=True))
|
|
147
|
+
return OrderedDict(sorted(element_counts.items()))
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def get_descriptors(
|
|
151
|
+
entry: Dict,
|
|
152
|
+
reaction_centers: str = "RC",
|
|
153
|
+
its: str = "ITS",
|
|
154
|
+
condensed: bool = True,
|
|
155
|
+
) -> Dict:
|
|
156
|
+
"""Enhance an entry dictionary with topology type and reaction type
|
|
157
|
+
descriptors.
|
|
158
|
+
|
|
159
|
+
:param entry: A dictionary with reaction data.
|
|
160
|
+
:type entry: Dict
|
|
161
|
+
:param reaction_centers: Key for accessing reaction center data.
|
|
162
|
+
:type reaction_centers: str
|
|
163
|
+
:param its: Key for accessing ITS (Intermediate Transition State) data.
|
|
164
|
+
:type its: str
|
|
165
|
+
|
|
166
|
+
:return: The enhanced entry with additional descriptors.
|
|
167
|
+
:rtype: Dict
|
|
168
|
+
"""
|
|
169
|
+
graph = GraphDescriptor._extract_graph(entry, reaction_centers)
|
|
170
|
+
its_graph = GraphDescriptor._extract_graph(entry, its)
|
|
171
|
+
|
|
172
|
+
if not graph or not its_graph:
|
|
173
|
+
return entry # Early exit if graphs are missing
|
|
174
|
+
|
|
175
|
+
# Set initial topology descriptor for the reaction center graph
|
|
176
|
+
entry["topo"] = GraphDescriptor.check_graph_type(graph)
|
|
177
|
+
entry["cycle"] = GraphDescriptor.get_cycle_member_rings(graph)
|
|
178
|
+
entry["atom_count"] = GraphDescriptor.get_element_count(graph)
|
|
179
|
+
entry["its_count"] = GraphDescriptor.get_element_count(its_graph)
|
|
180
|
+
|
|
181
|
+
# Determine the reaction type based on the topology type
|
|
182
|
+
entry["rtype"] = (
|
|
183
|
+
"Elementary"
|
|
184
|
+
if entry["topo"] in ["Single Cyclic", "Acyclic"]
|
|
185
|
+
else "Complicated"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
GraphDescriptor._adjust_cycle_and_step(entry, "cycle", entry["topo"])
|
|
189
|
+
entry["signature_rc"] = GraphSignature(graph).create_graph_signature()
|
|
190
|
+
|
|
191
|
+
# Initialize ITS descriptors and call adjust
|
|
192
|
+
topo_its = GraphDescriptor.check_graph_type(its_graph)
|
|
193
|
+
cycle_its = GraphDescriptor.get_cycle_member_rings(its_graph)
|
|
194
|
+
entry["cycle_its"] = cycle_its # Ensure key is initialized
|
|
195
|
+
GraphDescriptor._adjust_cycle_and_step(
|
|
196
|
+
entry, "cycle_its", topo_its, its_prefix="its"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
entry["signature_its"] = GraphSignature(its_graph).create_graph_signature()
|
|
200
|
+
|
|
201
|
+
return entry
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def _extract_graph(entry: Dict, key: str) -> Union[nx.Graph, None]:
|
|
205
|
+
"""Extracts a graph from an entry dictionary based on the specified
|
|
206
|
+
key.
|
|
207
|
+
|
|
208
|
+
:param entry: The dictionary containing graph data.
|
|
209
|
+
:type entry: Dict
|
|
210
|
+
:param key: The key for accessing graph data.
|
|
211
|
+
:type key: str
|
|
212
|
+
|
|
213
|
+
:return: The extracted graph or None if unavailable.
|
|
214
|
+
:rtype: Union[nx.Graph, None]
|
|
215
|
+
"""
|
|
216
|
+
data = entry.get(key)
|
|
217
|
+
if isinstance(data, tuple):
|
|
218
|
+
try:
|
|
219
|
+
return data[2]
|
|
220
|
+
except IndexError:
|
|
221
|
+
logger.error(f"No graph data available at index 2 for entry {entry}")
|
|
222
|
+
elif isinstance(data, nx.Graph):
|
|
223
|
+
return data
|
|
224
|
+
else:
|
|
225
|
+
logger.error(f"Unsupported data type for {key} in entry {entry}")
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def _adjust_cycle_and_step(
|
|
230
|
+
entry: Dict, cycle_key: str, topo_type: str, its_prefix: str = ""
|
|
231
|
+
) -> None:
|
|
232
|
+
"""Adjusts cycle and step descriptors based on the graph topology type.
|
|
233
|
+
|
|
234
|
+
:param entry: The entry dictionary to update.
|
|
235
|
+
:type entry: Dict
|
|
236
|
+
:param cycle_key: The key for the cycle descriptor.
|
|
237
|
+
:type cycle_key: str
|
|
238
|
+
:param topo_type: The topology type.
|
|
239
|
+
:type topo_type: str
|
|
240
|
+
:param its_prefix: Prefix for ITS-specific descriptors.
|
|
241
|
+
:type its_prefix: str
|
|
242
|
+
"""
|
|
243
|
+
step_key = f"rstep_{its_prefix}" if its_prefix else "rstep"
|
|
244
|
+
|
|
245
|
+
# Initialize the step key in the dictionary to avoid KeyError
|
|
246
|
+
if cycle_key not in entry:
|
|
247
|
+
entry[cycle_key] = []
|
|
248
|
+
|
|
249
|
+
if topo_type == "Acyclic":
|
|
250
|
+
entry[cycle_key] = [0]
|
|
251
|
+
elif topo_type == "Complex Cyclic":
|
|
252
|
+
entry[cycle_key] = [0] + entry[cycle_key]
|
|
253
|
+
|
|
254
|
+
entry[step_key] = len(entry[cycle_key])
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def _validate_graph_input(G: nx.Graph) -> None:
|
|
258
|
+
"""Validates that the input is a NetworkX graph.
|
|
259
|
+
|
|
260
|
+
:param G: The graph to validate.
|
|
261
|
+
:type G: nx.Graph
|
|
262
|
+
|
|
263
|
+
:raises TypeError: If G is not a NetworkX Graph.
|
|
264
|
+
"""
|
|
265
|
+
if not isinstance(G, nx.Graph):
|
|
266
|
+
raise TypeError("Input must be a NetworkX Graph object.")
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def process_entries_in_parallel(
|
|
270
|
+
entries: List[Dict],
|
|
271
|
+
reaction_centers: str = "RC",
|
|
272
|
+
its: str = "ITS",
|
|
273
|
+
condensed: bool = True,
|
|
274
|
+
n_jobs: int = 4,
|
|
275
|
+
verbose: int = 0,
|
|
276
|
+
) -> List[Dict]:
|
|
277
|
+
"""Processes a list of entries in parallel to enhance each entry with
|
|
278
|
+
descriptors.
|
|
279
|
+
|
|
280
|
+
:param entries: List of dictionaries containing reaction data to enhance.
|
|
281
|
+
:type entries: List[Dict]
|
|
282
|
+
:param reaction_centers: Key to retrieve reaction center graph data from each
|
|
283
|
+
entry dictionary.
|
|
284
|
+
:type reaction_centers: str
|
|
285
|
+
:param its: Key to retrieve ITS (Intermediate Transition State) graph data from
|
|
286
|
+
each entry dictionary.
|
|
287
|
+
:type its: str
|
|
288
|
+
:param condensed: If True, condenses node signatures with counts.
|
|
289
|
+
:type condensed: bool
|
|
290
|
+
:param n_jobs: Number of jobs to run in parallel. -1 uses all processors.
|
|
291
|
+
:type n_jobs: int
|
|
292
|
+
:param verbose: The verbosity level for joblib's Parallel.
|
|
293
|
+
:type verbose: int
|
|
294
|
+
|
|
295
|
+
:return: A list of enhanced dictionaries with added descriptors.
|
|
296
|
+
:rtype: List[Dict]
|
|
297
|
+
"""
|
|
298
|
+
return Parallel(n_jobs=n_jobs, verbose=verbose)(
|
|
299
|
+
delayed(GraphDescriptor.get_descriptors)(
|
|
300
|
+
entry, reaction_centers, its, condensed
|
|
301
|
+
)
|
|
302
|
+
for entry in entries
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def check_graph_connectivity(graph: nx.Graph) -> str:
|
|
307
|
+
"""Check the connectivity of a NetworkX graph.
|
|
308
|
+
|
|
309
|
+
This function assesses whether all nodes in the graph are connected by some path,
|
|
310
|
+
applicable to undirected graphs.
|
|
311
|
+
|
|
312
|
+
:param graph: A NetworkX graph object.
|
|
313
|
+
:type graph: nx.Graph
|
|
314
|
+
|
|
315
|
+
:return: Returns 'Connected' if the graph is connected, otherwise 'Disconnected'.
|
|
316
|
+
:rtype: str
|
|
317
|
+
|
|
318
|
+
:raises NetworkXNotImplemented: If graph is directed and does not support is_connected.
|
|
319
|
+
"""
|
|
320
|
+
if nx.is_connected(graph):
|
|
321
|
+
return "Connected"
|
|
322
|
+
else:
|
|
323
|
+
return "Disconnected."
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import networkx as nx
|
|
2
|
+
import hashlib
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GraphFP:
|
|
7
|
+
def __init__(
|
|
8
|
+
self, graph: nx.Graph, nBits: int = 1024, hash_alg: str = "sha256"
|
|
9
|
+
) -> None:
|
|
10
|
+
"""Initialize the GraphFP class to create binary fingerprints based on
|
|
11
|
+
various graph characteristics.
|
|
12
|
+
|
|
13
|
+
:param graph: Graph on which to perform analysis.
|
|
14
|
+
:type graph: nx.Graph
|
|
15
|
+
:param nBits: Size of the binary fingerprint in bits.
|
|
16
|
+
:type nBits: int
|
|
17
|
+
:param hash_alg: Cryptographic hash function used for hashing.
|
|
18
|
+
:type hash_alg: str
|
|
19
|
+
"""
|
|
20
|
+
self.graph = graph
|
|
21
|
+
self.nBits = nBits
|
|
22
|
+
self.hash_alg = hash_alg
|
|
23
|
+
self.hash_function = getattr(hashlib, self.hash_alg)
|
|
24
|
+
|
|
25
|
+
def fingerprint(self, method: str) -> str:
|
|
26
|
+
"""Generate a binary string fingerprint of the graph using the
|
|
27
|
+
specified method.
|
|
28
|
+
|
|
29
|
+
:param method: The method to use for fingerprinting
|
|
30
|
+
('spectrum', 'adjacency', 'degree', 'motif')
|
|
31
|
+
:type method: str
|
|
32
|
+
|
|
33
|
+
:return: A binary string of length `nBits` that represents the fingerprint of the graph.
|
|
34
|
+
:rtype: str
|
|
35
|
+
"""
|
|
36
|
+
if method == "spectrum":
|
|
37
|
+
fp = self._spectrum_fp()
|
|
38
|
+
elif method == "adjacency":
|
|
39
|
+
fp = self._adjacency_fp()
|
|
40
|
+
elif method == "degree":
|
|
41
|
+
fp = self._degree_sequence_fp()
|
|
42
|
+
elif method == "motif":
|
|
43
|
+
fp = self._motif_count_fp()
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError("Unsupported fingerprinting method.")
|
|
46
|
+
|
|
47
|
+
# If the fingerprint is shorter than nBits, use iterative deepening
|
|
48
|
+
if len(fp) < self.nBits:
|
|
49
|
+
fp += self.iterative_deepening(self.nBits - len(fp))
|
|
50
|
+
|
|
51
|
+
return fp[: self.nBits]
|
|
52
|
+
|
|
53
|
+
def _spectrum_fp(self) -> str:
|
|
54
|
+
# Graph spectrum (eigenvalues of the adjacency matrix)
|
|
55
|
+
eigenvalues = np.linalg.eigvals(nx.adjacency_matrix(self.graph).todense())
|
|
56
|
+
sorted_eigenvalues = np.sort(eigenvalues)[: self.nBits]
|
|
57
|
+
eigen_str = "".join(
|
|
58
|
+
bin(int(abs(eig)))[2:].zfill(8) for eig in sorted_eigenvalues
|
|
59
|
+
)
|
|
60
|
+
return eigen_str[: self.nBits]
|
|
61
|
+
|
|
62
|
+
def _adjacency_fp(self) -> str:
|
|
63
|
+
# Adjacency matrix flattened
|
|
64
|
+
adj_matrix = nx.adjacency_matrix(self.graph).todense().flatten()
|
|
65
|
+
adj_str = "".join(str(int(x)) for x in adj_matrix)
|
|
66
|
+
return adj_str[: self.nBits]
|
|
67
|
+
|
|
68
|
+
def _degree_sequence_fp(self) -> str:
|
|
69
|
+
# Degree sequence
|
|
70
|
+
degrees = sorted([d for n, d in self.graph.degree()], reverse=True)
|
|
71
|
+
degree_str = "".join(bin(d)[2:].zfill(8) for d in degrees)
|
|
72
|
+
return degree_str[: self.nBits]
|
|
73
|
+
|
|
74
|
+
def _motif_count_fp(self) -> str:
|
|
75
|
+
# Motif counts (e.g., number of triangles)
|
|
76
|
+
triangles = sum(nx.triangles(self.graph).values()) // 3
|
|
77
|
+
triangle_str = bin(triangles)[2:].zfill(self.nBits)
|
|
78
|
+
return triangle_str[: self.nBits]
|
|
79
|
+
|
|
80
|
+
def iterative_deepening(self, remaining_bits: int) -> str:
|
|
81
|
+
"""Extend the hash length using iterative hashing until the desired bit
|
|
82
|
+
length is achieved.
|
|
83
|
+
|
|
84
|
+
:param remaining_bits: Number of bits needed to complete the fingerprint
|
|
85
|
+
to `nBits`.
|
|
86
|
+
:type remaining_bits: int
|
|
87
|
+
|
|
88
|
+
:return: Additional binary data to achieve the desired hash length.
|
|
89
|
+
:rtype: str
|
|
90
|
+
"""
|
|
91
|
+
additional_data = ""
|
|
92
|
+
hash_obj = self.hash_function()
|
|
93
|
+
while len(additional_data) * 4 < remaining_bits:
|
|
94
|
+
hash_obj.update(additional_data.encode())
|
|
95
|
+
additional_data += hash_obj.hexdigest()
|
|
96
|
+
return bin(int(additional_data, 16))[2:][:remaining_bits]
|