synkit 1.6.5__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- synkit/CRN/Benchmark/__init__.py +53 -0
- synkit/CRN/Benchmark/crosschecks.py +13 -0
- synkit/CRN/Benchmark/kegg.py +12 -0
- synkit/CRN/Benchmark/networks.py +13 -0
- synkit/CRN/Benchmark/scaling.py +13 -0
- synkit/CRN/Benchmark/validate.py +13 -0
- synkit/CRN/Construct/__init__.py +16 -0
- synkit/CRN/Construct/abstract.py +668 -0
- synkit/CRN/Construct/arity.py +36 -0
- synkit/CRN/Construct/builder.py +619 -0
- synkit/CRN/Construct/derivation.py +63 -0
- synkit/CRN/Construct/flattener.py +100 -0
- synkit/CRN/Construct/keys.py +14 -0
- synkit/CRN/Construct/mixtures.py +117 -0
- synkit/CRN/Construct/rdkit_worker.py +69 -0
- synkit/CRN/Construct/smiles.py +99 -0
- synkit/CRN/Construct/state.py +31 -0
- synkit/CRN/Construct/strategy.py +75 -0
- synkit/CRN/Construct/worker.py +42 -0
- synkit/CRN/IO/__init__.py +36 -0
- synkit/CRN/IO/sbml.py +925 -0
- synkit/CRN/Pathway/__init__.py +72 -0
- synkit/CRN/Pathway/_adapter.py +333 -0
- synkit/CRN/Pathway/pathfinder.py +500 -0
- synkit/CRN/Pathway/reachability.py +796 -0
- synkit/CRN/Pathway/realizability.py +1322 -0
- synkit/CRN/Petrinet/__init__.py +93 -0
- synkit/CRN/Petrinet/analyzer.py +486 -0
- synkit/CRN/Petrinet/minimal_semiflows.py +436 -0
- synkit/CRN/Petrinet/net.py +1041 -0
- synkit/CRN/Petrinet/persistence.py +97 -0
- synkit/CRN/Petrinet/semiflows.py +483 -0
- synkit/CRN/Petrinet/siphon_search.py +402 -0
- synkit/CRN/Petrinet/structure.py +286 -0
- synkit/CRN/Props/__init__.py +130 -0
- synkit/CRN/Props/_rank.py +29 -0
- synkit/CRN/Props/deficiency.py +953 -0
- synkit/CRN/Props/dynamics.py +832 -0
- synkit/CRN/Props/helper.py +151 -0
- synkit/CRN/Props/stoich.py +751 -0
- synkit/CRN/Props/thermo.py +584 -0
- synkit/CRN/Query/__init__.py +66 -0
- synkit/CRN/Query/cache.py +41 -0
- synkit/CRN/Query/kegg_api.py +99 -0
- synkit/CRN/Query/kegg_extract.py +714 -0
- synkit/CRN/Query/kegg_impute.py +707 -0
- synkit/CRN/Query/kegg_parse.py +480 -0
- synkit/CRN/Query/to_syncrn.py +290 -0
- synkit/CRN/README.md +234 -0
- synkit/CRN/Structure/__init__.py +12 -0
- synkit/CRN/Structure/_graph_io.py +713 -0
- synkit/CRN/Structure/_matrices.py +88 -0
- synkit/CRN/Structure/_parse.py +544 -0
- synkit/CRN/Structure/reaction.py +237 -0
- synkit/CRN/Structure/rule.py +55 -0
- synkit/CRN/Structure/species.py +46 -0
- synkit/CRN/Structure/syncrn.py +929 -0
- synkit/CRN/Symmetry/__init__.py +20 -0
- synkit/CRN/Symmetry/_common.py +1059 -0
- synkit/CRN/Symmetry/_ir.py +940 -0
- synkit/CRN/Symmetry/automorphism.py +472 -0
- synkit/CRN/Symmetry/canon.py +441 -0
- synkit/CRN/Symmetry/isomorphism.py +412 -0
- synkit/CRN/Symmetry/symmetry.py +61 -0
- synkit/CRN/Symmetry/wl_canon.py +967 -0
- synkit/CRN/Visualize/__init__.py +11 -0
- synkit/CRN/Visualize/labels.py +113 -0
- synkit/CRN/Visualize/layout.py +708 -0
- synkit/CRN/Visualize/palette.py +292 -0
- synkit/CRN/Visualize/validation.py +122 -0
- synkit/CRN/Visualize/vis.py +1152 -0
- synkit/CRN/__init__.py +233 -0
- synkit/CRN/_build.py +50 -0
- synkit/CRN/_native_build.py +175 -0
- synkit/CRN/analysis.py +629 -0
- synkit/CRN/backend.py +74 -0
- synkit/CRN/cpp/CMakeLists.txt +58 -0
- synkit/CRN/cpp/algebra.cpp +46 -0
- synkit/CRN/cpp/closure.cpp +29 -0
- synkit/CRN/cpp/exact.h +55 -0
- synkit/CRN/cpp/pathway.cpp +46 -0
- synkit/CRN/cpp/semiflows.cpp +209 -0
- synkit/CRN/cpp/structural.cpp +240 -0
- synkit/CRN/cpp/symmetry.cpp +45 -0
- synkit/CRN/cpp/write_manifest.cmake +28 -0
- synkit/CRN/kinds.py +137 -0
- synkit/CRN/lib/synkit_crn.dll +0 -0
- synkit/CRN/lib/synkit_crn.json +33 -0
- synkit/CRN/native.py +388 -0
- synkit/CRN/native_analysis.py +459 -0
- synkit/Chem/Cluster/__init__.py +0 -0
- synkit/Chem/Cluster/butina.py +140 -0
- synkit/Chem/Fingerprint/__init__.py +0 -0
- synkit/Chem/Fingerprint/fp_calculator.py +155 -0
- synkit/Chem/Fingerprint/smiles_featurizer.py +258 -0
- synkit/Chem/Fingerprint/transformation_fp.py +139 -0
- synkit/Chem/Mapper/README.rst +43 -0
- synkit/Chem/Mapper/__init__.py +255 -0
- synkit/Chem/Mapper/_compat.py +130 -0
- synkit/Chem/Mapper/adapters/__init__.py +1 -0
- synkit/Chem/Mapper/adapters/prediction.py +126 -0
- synkit/Chem/Mapper/adapters/template.py +412 -0
- synkit/Chem/Mapper/chem/__init__.py +52 -0
- synkit/Chem/Mapper/chem/aam.py +673 -0
- synkit/Chem/Mapper/chem/blind.py +160 -0
- synkit/Chem/Mapper/chem/its.py +309 -0
- synkit/Chem/Mapper/chem/smiles.py +564 -0
- synkit/Chem/Mapper/graph/__init__.py +66 -0
- synkit/Chem/Mapper/graph/automorphism.py +395 -0
- synkit/Chem/Mapper/graph/blockcut.py +98 -0
- synkit/Chem/Mapper/graph/labeled_graph.py +104 -0
- synkit/Chem/Mapper/graph/refine.py +176 -0
- synkit/Chem/Mapper/graph/synkit_adapter.py +110 -0
- synkit/Chem/Mapper/io/__init__.py +13 -0
- synkit/Chem/Mapper/io/idxmapstr.py +82 -0
- synkit/Chem/Mapper/mapper.py +685 -0
- synkit/Chem/Mapper/reaction_search.py +554 -0
- synkit/Chem/Mapper/reporting/__init__.py +1 -0
- synkit/Chem/Mapper/reporting/alternatives.py +451 -0
- synkit/Chem/Mapper/reporting/analysis.py +681 -0
- synkit/Chem/Mapper/reporting/native_analysis.py +485 -0
- synkit/Chem/Mapper/reporting/spectrum.py +642 -0
- synkit/Chem/Mapper/solvers/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/__init__.py +141 -0
- synkit/Chem/Mapper/solvers/exact/assignment/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/assignment/assignment_certificate.py +113 -0
- synkit/Chem/Mapper/solvers/exact/assignment/assignment_domains.py +162 -0
- synkit/Chem/Mapper/solvers/exact/assignment/assignment_verification.py +58 -0
- synkit/Chem/Mapper/solvers/exact/assignment/incremental_assignment.py +247 -0
- synkit/Chem/Mapper/solvers/exact/certificate.py +128 -0
- synkit/Chem/Mapper/solvers/exact/core.py +183 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/bounds.py +319 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/exhaustive.py +179 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/inputs.py +283 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/records.py +242 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/search.py +1025 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/verify.py +378 -0
- synkit/Chem/Mapper/solvers/exact/edit_support.py +375 -0
- synkit/Chem/Mapper/solvers/exact/hybrid.py +224 -0
- synkit/Chem/Mapper/solvers/exact/hydrogen.py +264 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/branching.py +499 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumerate.py +1050 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumeration_result.py +30 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/kernel.py +256 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/milp.py +194 -0
- synkit/Chem/Mapper/solvers/exact/native/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/native/build.py +154 -0
- synkit/Chem/Mapper/solvers/exact/native/candidates.py +593 -0
- synkit/Chem/Mapper/solvers/exact/native/canonical.py +396 -0
- synkit/Chem/Mapper/solvers/exact/native/disk_frontier.py +415 -0
- synkit/Chem/Mapper/solvers/exact/native/frontier.py +390 -0
- synkit/Chem/Mapper/solvers/exact/native/hydrogen_quotient.py +213 -0
- synkit/Chem/Mapper/solvers/exact/native/its.py +528 -0
- synkit/Chem/Mapper/solvers/exact/native/native_distance.cpp +2803 -0
- synkit/Chem/Mapper/solvers/exact/native/orbit_aggregation.py +328 -0
- synkit/Chem/Mapper/solvers/exact/native/parallel.py +205 -0
- synkit/Chem/Mapper/solvers/exact/native/pmcd.py +203 -0
- synkit/Chem/Mapper/solvers/exact/native/runtime.py +40 -0
- synkit/Chem/Mapper/solvers/exact/native/search.py +294 -0
- synkit/Chem/Mapper/solvers/exact/native/separator.py +224 -0
- synkit/Chem/Mapper/solvers/exact/native_build.py +12 -0
- synkit/Chem/Mapper/solvers/exact/pabs/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/pabs/cost_lattice.py +43 -0
- synkit/Chem/Mapper/solvers/exact/pabs/factor_spectrum.py +110 -0
- synkit/Chem/Mapper/solvers/exact/pabs/multi_shell.py +368 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation.py +679 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_bounds.py +436 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_limits.py +13 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_search.py +856 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_symmetry.py +66 -0
- synkit/Chem/Mapper/solvers/exact/pabs/residual_typed_bonds.py +136 -0
- synkit/Chem/Mapper/solvers/exact/pabs/reward_frontier.py +201 -0
- synkit/Chem/Mapper/solvers/exact/pabs/separator_bound.py +258 -0
- synkit/Chem/Mapper/solvers/exact/pabs/separator_spectrum.py +308 -0
- synkit/Chem/Mapper/solvers/exact/pabs/sparse_reward_bound.py +207 -0
- synkit/Chem/Mapper/solvers/exact/pabs/suffix_spectrum.py +299 -0
- synkit/Chem/Mapper/solvers/exact/search.py +55 -0
- synkit/Chem/Mapper/solvers/exact/seeds/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/seeds/seed.py +104 -0
- synkit/Chem/Mapper/solvers/exact/seeds/seed_fragments.py +148 -0
- synkit/Chem/Mapper/solvers/exact/seeds/seed_relaxation.py +181 -0
- synkit/Chem/Mapper/solvers/exact/symmetry.py +223 -0
- synkit/Chem/Mapper/solvers/native/__init__.py +4 -0
- synkit/Chem/Mapper/solvers/native/assignment.py +99 -0
- synkit/Chem/Mapper/solvers/native/astar.py +22 -0
- synkit/Chem/Mapper/solvers/native/build.py +385 -0
- synkit/Chem/Mapper/solvers/native/classification.py +641 -0
- synkit/Chem/Mapper/solvers/native/core.py +378 -0
- synkit/Chem/Mapper/solvers/native/cpp/CMakeLists.txt +120 -0
- synkit/Chem/Mapper/solvers/native/cpp/examples/map_reaction.cpp +29 -0
- synkit/Chem/Mapper/solvers/native/cpp/examples/slap_graph.cpp +63 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/assignment/assignment.h +18 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/astar.h +18 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/bound.h +33 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/pmcd_witness.h +16 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/runtime.h +130 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/hydrogen/hydrogen.h +73 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mapper.h +175 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mces/mces.h +9 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/pabs/seed.h +34 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/reaction/reaction.h +30 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/sepdp/sepdp.h +21 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/slap.h +72 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/solvers/solvers.h +16 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/classification.h +140 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/cpabs.h +33 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/assignment/abb.cpp +302 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/CMakeLists.txt +12 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/README.md +47 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar.cpp +228 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar_graph.cpp +37 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/bound.cpp +212 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/core/dispatch.cpp +272 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/core/pmcd_witness.cpp +36 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/hydrogen/hydrogen.cpp +380 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/mces/README.md +57 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/mces/mces.cpp +287 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/bridge.cpp +334 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/chemical_floor.inc +181 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/endpoint_symmetry.inc +104 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/exact_helpers.h +20 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_assignment_certificate.h +165 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_certificate.h +125 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_model.h +365 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_provider.inc +319 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_rounding.inc +170 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_test_hooks.h +342 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/objective_reduction.inc +137 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/overlap_dual.inc +351 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/profile_seed.inc +214 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/seed_heuristics.inc +640 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_embedding.cpp +430 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_root_lp.h +176 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/test_hooks.inc +262 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/reaction/reaction.cpp +611 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/README.md +61 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/sepdp.cpp +253 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/slap.cpp +810 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/formulation.h +138 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/maxsat.cpp +526 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/ortools.cpp +475 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/seed.h +80 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/weighted_cnf.h +256 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/supporting/README.md +43 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/supporting/dfs.cpp +123 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/supporting/edit_support.cpp +224 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification.cpp +553 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_pipeline.cpp +124 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_stream.h +13 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/cpabs.cpp +455 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/edge_deck.cpp +743 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naugraph.c +732 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.c +94 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.h +49 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.c +1771 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.h +133 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nautil.c +750 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naututil.h +355 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.c +1256 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.h +1544 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.c +1131 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.h +70 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/sorttemplates.c +580 -0
- synkit/Chem/Mapper/solvers/native/hydrogen.py +711 -0
- synkit/Chem/Mapper/solvers/native/implicit.py +506 -0
- synkit/Chem/Mapper/solvers/native/implicit_certificate.py +187 -0
- synkit/Chem/Mapper/solvers/native/implicit_preparation.py +309 -0
- synkit/Chem/Mapper/solvers/native/lib/synkit_mapper.dll +0 -0
- synkit/Chem/Mapper/solvers/native/mces.py +16 -0
- synkit/Chem/Mapper/solvers/native/reaction.py +179 -0
- synkit/Chem/Mapper/solvers/native/reaction_build.py +277 -0
- synkit/Chem/Mapper/solvers/native/reaction_text.py +26 -0
- synkit/Chem/Mapper/solvers/native/runtime.py +242 -0
- synkit/Chem/Mapper/solvers/native/sepdp.py +47 -0
- synkit/Chem/Mapper/solvers/native/slap.py +266 -0
- synkit/Chem/Mapper/solvers/native/solvers.py +67 -0
- synkit/Chem/Mapper/solvers/native/stereo.py +86 -0
- synkit/Chem/Mapper/solvers/native/supporting.py +34 -0
- synkit/Chem/Mapper/solvers/native/symmetry.py +91 -0
- synkit/Chem/Mapper/solvers/slap/__init__.py +29 -0
- synkit/Chem/Mapper/solvers/slap/lap.py +174 -0
- synkit/Chem/Mapper/solvers/slap/sequential.py +696 -0
- synkit/Chem/Mapper/validation/__init__.py +1 -0
- synkit/Chem/Mapper/validation/aam_validator.py +442 -0
- synkit/Chem/Mapper/validation/annotations.py +274 -0
- synkit/Chem/Mapper/validation/bonds.py +124 -0
- synkit/Chem/Mapper/validation/cohorts.py +72 -0
- synkit/Chem/Mapper/validation/identifiability.py +150 -0
- synkit/Chem/Mapper/validation/numerical_scope.py +36 -0
- synkit/Chem/Mapper/validation/orbits.py +137 -0
- synkit/Chem/Molecule/__init__.py +0 -0
- synkit/Chem/Molecule/_chirality_loci.py +165 -0
- synkit/Chem/Molecule/_chirality_types.py +231 -0
- synkit/Chem/Molecule/_helical_loci.py +167 -0
- synkit/Chem/Molecule/_stereo_axis_evidence.py +202 -0
- synkit/Chem/Molecule/_stereo_element_detection.py +193 -0
- synkit/Chem/Molecule/_stereo_orientation_constraints.py +511 -0
- synkit/Chem/Molecule/_tetrahedral_carriers.py +63 -0
- synkit/Chem/Molecule/atom_features.py +379 -0
- synkit/Chem/Molecule/chirality.py +1041 -0
- synkit/Chem/Molecule/cip_assignment.py +569 -0
- synkit/Chem/Molecule/cip_ranking.py +843 -0
- synkit/Chem/Molecule/coordinate_stereo.py +503 -0
- synkit/Chem/Molecule/derived_stereo_naming.py +165 -0
- synkit/Chem/Molecule/descriptors.py +452 -0
- synkit/Chem/Molecule/formula.py +249 -0
- synkit/Chem/Molecule/global_stereo.py +175 -0
- synkit/Chem/Molecule/graph_annotator.py +300 -0
- synkit/Chem/Molecule/standardize.py +817 -0
- synkit/Chem/Molecule/stereo_evidence.py +179 -0
- synkit/Chem/Molecule/stereo_perception.py +1032 -0
- synkit/Chem/Molecule/valence.py +123 -0
- synkit/Chem/Reaction/Mapper/__init__.py +44 -0
- synkit/Chem/Reaction/__init__.py +20 -0
- synkit/Chem/Reaction/aam_validator.py +5 -0
- synkit/Chem/Reaction/balance_check.py +138 -0
- synkit/Chem/Reaction/canon_rsmi.py +249 -0
- synkit/Chem/Reaction/cleaning.py +58 -0
- synkit/Chem/Reaction/deionize.py +199 -0
- synkit/Chem/Reaction/explicit_h_audit.py +232 -0
- synkit/Chem/Reaction/fix_aam.py +64 -0
- synkit/Chem/Reaction/neutralize.py +207 -0
- synkit/Chem/Reaction/radical_wildcard.py +224 -0
- synkit/Chem/Reaction/standardize.py +203 -0
- synkit/Chem/Reaction/tautomerize.py +205 -0
- synkit/Chem/__init__.py +1 -0
- synkit/Chem/utils.py +315 -0
- synkit/Data/__init__.py +0 -0
- synkit/Data/aldol.json.gz +1 -0
- synkit/Data/gen_partial_aam.py +147 -0
- synkit/Data/paracetamol.json.gz +1 -0
- synkit/Graph/Canon/__init__.py +16 -0
- synkit/Graph/Canon/_native/__init__.py +13 -0
- synkit/Graph/Canon/_native/build.py +93 -0
- synkit/Graph/Canon/_native/lib/synkit_canon.dll +0 -0
- synkit/Graph/Canon/_native/prepare.py +49 -0
- synkit/Graph/Canon/_native/runtime.py +140 -0
- synkit/Graph/Canon/_native/search.py +137 -0
- synkit/Graph/Canon/canon_algs.py +228 -0
- synkit/Graph/Canon/canon_graph.py +531 -0
- synkit/Graph/Canon/cpp/CMakeLists.txt +42 -0
- synkit/Graph/Canon/cpp/README.md +123 -0
- synkit/Graph/Canon/cpp/examples/canonical_graph.cpp +16 -0
- synkit/Graph/Canon/cpp/include/synkit_canon/canon.h +85 -0
- synkit/Graph/Canon/cpp/src/canonical.cpp +391 -0
- synkit/Graph/Canon/cpp/src/symmetry.h +83 -0
- synkit/Graph/Canon/cpp/tests/symmetry.cpp +93 -0
- synkit/Graph/Canon/exact.py +1020 -0
- synkit/Graph/Canon/exact_refinement.py +368 -0
- synkit/Graph/Canon/nauty.py +143 -0
- synkit/Graph/Context/__init__.py +0 -0
- synkit/Graph/Context/hier_context.py +244 -0
- synkit/Graph/Context/radius_expand.py +252 -0
- synkit/Graph/FG/__init__.py +19 -0
- synkit/Graph/FG/api.py +39 -0
- synkit/Graph/FG/audit.py +155 -0
- synkit/Graph/FG/catalog.py +919 -0
- synkit/Graph/FG/detector.py +288 -0
- synkit/Graph/FG/model.py +97 -0
- synkit/Graph/FG/recognizers.py +274 -0
- synkit/Graph/FG/ring_system.py +154 -0
- synkit/Graph/Feature/Descriptors/topology.py +857 -0
- synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
- synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +222 -0
- synkit/Graph/Feature/__init__.py +5 -0
- synkit/Graph/Feature/graph_descriptors.py +323 -0
- synkit/Graph/Feature/graph_fps.py +96 -0
- synkit/Graph/Feature/graph_signature.py +239 -0
- synkit/Graph/Feature/hash_fps.py +133 -0
- synkit/Graph/Feature/morgan_fps.py +88 -0
- synkit/Graph/Feature/path_fps.py +81 -0
- synkit/Graph/Feature/wl_hash.py +134 -0
- synkit/Graph/Fusion/__init__.py +85 -0
- synkit/Graph/Fusion/candidate.py +559 -0
- synkit/Graph/Fusion/constructor.py +877 -0
- synkit/Graph/Fusion/identity.py +184 -0
- synkit/Graph/Fusion/interface.py +700 -0
- synkit/Graph/Fusion/search.py +241 -0
- synkit/Graph/Hyrogen/__init__.py +6 -0
- synkit/Graph/Hyrogen/_misc.py +591 -0
- synkit/Graph/Hyrogen/hcomplete.py +995 -0
- synkit/Graph/Hyrogen/hcomplete_legacy.py +368 -0
- synkit/Graph/Hyrogen/hcompletion_algorithms.py +713 -0
- synkit/Graph/Hyrogen/hextend.py +570 -0
- synkit/Graph/Hyrogen/hextend_legacy.py +215 -0
- synkit/Graph/ITS/__init__.py +4 -0
- synkit/Graph/ITS/its_builder.py +120 -0
- synkit/Graph/ITS/its_construction.py +719 -0
- synkit/Graph/ITS/its_decompose.py +572 -0
- synkit/Graph/ITS/its_destruction.py +331 -0
- synkit/Graph/ITS/its_expand.py +1251 -0
- synkit/Graph/ITS/its_relabel.py +187 -0
- synkit/Graph/ITS/its_reverter.py +356 -0
- synkit/Graph/ITS/normalize_aam.py +145 -0
- synkit/Graph/ITS/partial_its.py +238 -0
- synkit/Graph/ITS/rc_extractor.py +656 -0
- synkit/Graph/ITS/stereo.py +74 -0
- synkit/Graph/MTG/__init__.py +107 -0
- synkit/Graph/MTG/gluing.py +451 -0
- synkit/Graph/MTG/history.py +928 -0
- synkit/Graph/MTG/inventory.py +118 -0
- synkit/Graph/MTG/local.py +447 -0
- synkit/Graph/MTG/mcs_matcher.py +246 -0
- synkit/Graph/MTG/mtg.py +611 -0
- synkit/Graph/MTG/mtg_explore.py +74 -0
- synkit/Graph/MTG/observation.py +209 -0
- synkit/Graph/MTG/process.py +577 -0
- synkit/Graph/MTG/queries.py +66 -0
- synkit/Graph/MTG/realization.py +95 -0
- synkit/Graph/MTG/series_parallel.py +107 -0
- synkit/Graph/MTG/utils.py +424 -0
- synkit/Graph/Matcher/__init__.py +10 -0
- synkit/Graph/Matcher/approx_mcs.py +1264 -0
- synkit/Graph/Matcher/auto_est.py +736 -0
- synkit/Graph/Matcher/automorphism.py +314 -0
- synkit/Graph/Matcher/batch_cluster.py +224 -0
- synkit/Graph/Matcher/dedup_matches.py +247 -0
- synkit/Graph/Matcher/graph_cluster.py +191 -0
- synkit/Graph/Matcher/graph_matcher.py +312 -0
- synkit/Graph/Matcher/graph_morphism.py +421 -0
- synkit/Graph/Matcher/mcs_matcher.py +804 -0
- synkit/Graph/Matcher/multi_turbo_iso.py +186 -0
- synkit/Graph/Matcher/orbit.py +284 -0
- synkit/Graph/Matcher/partial_matcher.py +694 -0
- synkit/Graph/Matcher/sing.py +544 -0
- synkit/Graph/Matcher/subgraph_matcher.py +812 -0
- synkit/Graph/Matcher/turbo_iso.py +272 -0
- synkit/Graph/Matcher/wl_sel.py +537 -0
- synkit/Graph/Mech/__init__.py +26 -0
- synkit/Graph/Mech/conversion.py +1466 -0
- synkit/Graph/Mech/conversion_debug.py +237 -0
- synkit/Graph/Mech/electron_accounting.py +200 -0
- synkit/Graph/Mech/lwg_editor.py +406 -0
- synkit/Graph/Mech/lwg_ops.py +313 -0
- synkit/Graph/Morphism/__init__.py +99 -0
- synkit/Graph/Morphism/constraints.py +460 -0
- synkit/Graph/Morphism/llg.py +999 -0
- synkit/Graph/Morphism/morphism.py +349 -0
- synkit/Graph/Morphism/stereo.py +258 -0
- synkit/Graph/Morphism/stereo_morphism.py +383 -0
- synkit/Graph/Morphism/stereo_morphism_helpers.py +957 -0
- synkit/Graph/Morphism/stereo_morphism_types.py +117 -0
- synkit/Graph/Stereo/__init__.py +376 -0
- synkit/Graph/Stereo/_descriptor_core.py +207 -0
- synkit/Graph/Stereo/_descriptor_serialization.py +138 -0
- synkit/Graph/Stereo/canonical.py +648 -0
- synkit/Graph/Stereo/changes.py +964 -0
- synkit/Graph/Stereo/composition.py +391 -0
- synkit/Graph/Stereo/configured.py +946 -0
- synkit/Graph/Stereo/couplings.py +245 -0
- synkit/Graph/Stereo/descriptors.py +1030 -0
- synkit/Graph/Stereo/enumeration.py +597 -0
- synkit/Graph/Stereo/extended_descriptors.py +406 -0
- synkit/Graph/Stereo/global_configured.py +152 -0
- synkit/Graph/Stereo/global_stereo.py +304 -0
- synkit/Graph/Stereo/identity.py +648 -0
- synkit/Graph/Stereo/interchange.py +496 -0
- synkit/Graph/Stereo/legacy.py +460 -0
- synkit/Graph/Stereo/matching.py +899 -0
- synkit/Graph/Stereo/native.py +154 -0
- synkit/Graph/Stereo/orbits.py +668 -0
- synkit/Graph/Stereo/outcomes.py +99 -0
- synkit/Graph/Stereo/pairwise.py +350 -0
- synkit/Graph/Stereo/rdkit_adapter.py +662 -0
- synkit/Graph/Stereo/rdkit_identity.py +118 -0
- synkit/Graph/Stereo/rdkit_resonance.py +140 -0
- synkit/Graph/Stereo/reaction_interchange.py +266 -0
- synkit/Graph/Stereo/semantics.py +296 -0
- synkit/Graph/Stereo/supports.py +257 -0
- synkit/Graph/Stereo/wire.py +339 -0
- synkit/Graph/Wildcard/__init__.py +0 -0
- synkit/Graph/Wildcard/fuse_graph.py +156 -0
- synkit/Graph/Wildcard/graph_wc.py +303 -0
- synkit/Graph/Wildcard/its_merge.py +857 -0
- synkit/Graph/Wildcard/radwc.py +114 -0
- synkit/Graph/Wildcard/wc_matcher.py +576 -0
- synkit/Graph/Wildcard/wildcard.py +225 -0
- synkit/Graph/__init__.py +17 -0
- synkit/Graph/canon_graph.py +14 -0
- synkit/Graph/syn_graph.py +199 -0
- synkit/Graph/utils.py +324 -0
- synkit/IO/__init__.py +4 -0
- synkit/IO/chem_converter.py +1059 -0
- synkit/IO/combinatorial/__init__.py +8 -0
- synkit/IO/combinatorial/gml_to_graph.py +223 -0
- synkit/IO/combinatorial/graph_to_gml.py +280 -0
- synkit/IO/combinatorial/graph_to_smarts.py +186 -0
- synkit/IO/combinatorial/smarts_expander.py +147 -0
- synkit/IO/combinatorial/smarts_generalizer.py +133 -0
- synkit/IO/combinatorial/smarts_to_graph.py +179 -0
- synkit/IO/conversion.py +42 -0
- synkit/IO/data_io.py +269 -0
- synkit/IO/data_process.py +143 -0
- synkit/IO/debug.py +73 -0
- synkit/IO/gml_to_nx.py +150 -0
- synkit/IO/graph_to_mol.py +199 -0
- synkit/IO/mol_to_graph.py +1105 -0
- synkit/IO/mol_to_graph_chemistry.py +545 -0
- synkit/IO/nx_to_gml.py +209 -0
- synkit/IO/smiles_to_id.py +123 -0
- synkit/Mechanism/__init__.py +151 -0
- synkit/Mechanism/adapters.py +136 -0
- synkit/Mechanism/audit.py +323 -0
- synkit/Mechanism/benchmark.py +638 -0
- synkit/Mechanism/compatibility.py +13 -0
- synkit/Mechanism/drawing.py +85 -0
- synkit/Mechanism/electron_models.py +725 -0
- synkit/Mechanism/equivalence.py +521 -0
- synkit/Mechanism/evidence.py +262 -0
- synkit/Mechanism/interchange.py +141 -0
- synkit/Mechanism/model.py +918 -0
- synkit/Mechanism/neighbor_stereo.py +312 -0
- synkit/Mechanism/radical_data.py +740 -0
- synkit/Mechanism/replay.py +986 -0
- synkit/Mechanism/schema.py +264 -0
- synkit/Mechanism/stereo_state.py +416 -0
- synkit/Mechanism/symbols.py +91 -0
- synkit/Rule/Apply/__init__.py +35 -0
- synkit/Rule/Apply/conditions.py +45 -0
- synkit/Rule/Apply/dpo.py +998 -0
- synkit/Rule/Apply/rule_matcher.py +218 -0
- synkit/Rule/Apply/validation.py +354 -0
- synkit/Rule/Compose/__init__.py +106 -0
- synkit/Rule/Compose/_identity.py +43 -0
- synkit/Rule/Compose/composition.py +976 -0
- synkit/Rule/Compose/laws.py +439 -0
- synkit/Rule/Compose/search.py +771 -0
- synkit/Rule/Compose/synrule.py +170 -0
- synkit/Rule/Modify/__init__.py +0 -0
- synkit/Rule/Modify/implict_rule.py +56 -0
- synkit/Rule/Modify/longest_path.py +92 -0
- synkit/Rule/Modify/molecule_rule.py +115 -0
- synkit/Rule/Modify/prune_templates.py +78 -0
- synkit/Rule/Modify/rule_utils.py +194 -0
- synkit/Rule/Modify/strip_rule.py +97 -0
- synkit/Rule/__init__.py +28 -0
- synkit/Rule/generic_stereo.py +884 -0
- synkit/Rule/stereo_identity.py +271 -0
- synkit/Rule/syn_rule.py +1090 -0
- synkit/Synthesis/MSR/__init__.py +0 -0
- synkit/Synthesis/MSR/path_finder.py +239 -0
- synkit/Synthesis/Metrics/__init__.py +0 -0
- synkit/Synthesis/Metrics/_base.py +52 -0
- synkit/Synthesis/Metrics/_plot.py +129 -0
- synkit/Synthesis/Metrics/_ranking.py +179 -0
- synkit/Synthesis/RBL/__init__.py +80 -0
- synkit/Synthesis/RBL/engine.py +1249 -0
- synkit/Synthesis/RBL/matching.py +652 -0
- synkit/Synthesis/RBL/overlap.py +336 -0
- synkit/Synthesis/RBL/pipeline.py +376 -0
- synkit/Synthesis/RBL/policy.py +139 -0
- synkit/Synthesis/RBL/proof.py +370 -0
- synkit/Synthesis/RBL/reaction.py +506 -0
- synkit/Synthesis/RBL/state.py +169 -0
- synkit/Synthesis/RBL/validation.py +1060 -0
- synkit/Synthesis/Reactor/__init__.py +60 -0
- synkit/Synthesis/Reactor/core/__init__.py +1 -0
- synkit/Synthesis/Reactor/core/engine.py +967 -0
- synkit/Synthesis/Reactor/core/product.py +621 -0
- synkit/Synthesis/Reactor/core/rewrite.py +561 -0
- synkit/Synthesis/Reactor/core/strategy.py +44 -0
- synkit/Synthesis/Reactor/matching/__init__.py +1 -0
- synkit/Synthesis/Reactor/matching/mixin.py +920 -0
- synkit/Synthesis/Reactor/matching/policy.py +345 -0
- synkit/Synthesis/Reactor/matching/symmetry.py +309 -0
- synkit/Synthesis/Reactor/output/__init__.py +1 -0
- synkit/Synthesis/Reactor/output/deduplication.py +703 -0
- synkit/Synthesis/Reactor/output/policy.py +16 -0
- synkit/Synthesis/Reactor/output/serialization.py +485 -0
- synkit/Synthesis/Reactor/output/structural.py +893 -0
- synkit/Synthesis/Reactor/stereo/__init__.py +1 -0
- synkit/Synthesis/Reactor/stereo/assignment.py +35 -0
- synkit/Synthesis/Reactor/stereo/mixin.py +780 -0
- synkit/Synthesis/Reactor/variants/__init__.py +1 -0
- synkit/Synthesis/Reactor/variants/imbalanced.py +180 -0
- synkit/Synthesis/Reactor/variants/partial.py +84 -0
- synkit/Synthesis/Reactor/workflow/__init__.py +1 -0
- synkit/Synthesis/Reactor/workflow/batch.py +550 -0
- synkit/Synthesis/Reactor/workflow/benchmark.py +143 -0
- synkit/Synthesis/Reactor/workflow/postprocess.py +267 -0
- synkit/Synthesis/Reactor/workflow/rule_filter.py +210 -0
- synkit/Synthesis/__init__.py +0 -0
- synkit/Synthesis/reactor_utils.py +316 -0
- synkit/Utils/__init__.py +0 -0
- synkit/Utils/utils.py +172 -0
- synkit/Vis/__init__.py +91 -0
- synkit/Vis/crn/__init__.py +40 -0
- synkit/Vis/epd/__init__.py +20 -0
- synkit/Vis/epd/arrows.py +425 -0
- synkit/Vis/epd/chem.py +197 -0
- synkit/Vis/epd/constants.py +83 -0
- synkit/Vis/epd/layout.py +283 -0
- synkit/Vis/epd/mapping.py +51 -0
- synkit/Vis/epd/models.py +136 -0
- synkit/Vis/epd/render.py +543 -0
- synkit/Vis/epd/utils.py +108 -0
- synkit/Vis/epd/visualizer.py +811 -0
- synkit/Vis/graph_visualizer.py +375 -0
- synkit/Vis/its/__init__.py +19 -0
- synkit/Vis/its/drawer.py +615 -0
- synkit/Vis/its_drawer.py +35 -0
- synkit/Vis/molecule/__init__.py +31 -0
- synkit/Vis/molecule/drawer.py +563 -0
- synkit/Vis/molecule/reaction.py +285 -0
- synkit/Vis/molecule_drawer.py +33 -0
- synkit/Vis/mtg/__init__.py +17 -0
- synkit/Vis/mtg/drawer.py +769 -0
- synkit/Vis/mtg_drawer.py +34 -0
- synkit/Vis/pdf_writer.py +146 -0
- synkit/Vis/reaction/__init__.py +19 -0
- synkit/Vis/reaction/rule.py +141 -0
- synkit/Vis/reaction/rxn.py +532 -0
- synkit/Vis/reaction_drawer.py +41 -0
- synkit/Vis/space/__init__.py +17 -0
- synkit/Vis/space/chemical.py +80 -0
- synkit/Vis/space/embedding.py +87 -0
- synkit/Vis/vis_synedu/Vis/__init__.py +7 -0
- synkit/Vis/vis_synedu/Vis/dpo.py +862 -0
- synkit/Vis/vis_synedu/rxn_vis.py +377 -0
- synkit/Vis/vis_synedu/vis.py +501 -0
- synkit/Vis/visual_drawer.py +215 -0
- synkit/Vis/visual_model.py +520 -0
- synkit/__init__.py +3 -0
- synkit/_native.py +91 -0
- synkit/examples.py +50 -0
- synkit/version.py +23 -0
- synkit-1.6.5.dist-info/METADATA +156 -0
- synkit-1.6.5.dist-info/RECORD +636 -0
- synkit-1.6.5.dist-info/WHEEL +4 -0
- synkit-1.6.5.dist-info/licenses/LICENSE +21 -0
- synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
- synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""automorphism.py
|
|
2
|
+
~~~~~~~~~~~~~~~~~~
|
|
3
|
+
Utility for computing graph automorphisms and pruning redundant sub-graph
|
|
4
|
+
mappings equivalent under those symmetries.
|
|
5
|
+
|
|
6
|
+
This module provides the :class:`Automorphism` helper, which computes the
|
|
7
|
+
node-orbits of a graph and uses them to deduplicate subgraph-match mappings.
|
|
8
|
+
|
|
9
|
+
Key idea
|
|
10
|
+
--------
|
|
11
|
+
Group host nodes into **orbits** under the automorphism group of the host
|
|
12
|
+
graph: two nodes are in the same orbit if there exists an automorphism
|
|
13
|
+
:math:`\\sigma` such that :math:`\\sigma(u) = v`.
|
|
14
|
+
|
|
15
|
+
Mappings are considered equivalent if they hit the same **multiset of orbits**
|
|
16
|
+
on the host side, and a single representative is kept.
|
|
17
|
+
|
|
18
|
+
Disconnected graphs
|
|
19
|
+
-------------------
|
|
20
|
+
If the host graph is disconnected, we choose one connected component as an
|
|
21
|
+
**anchor** (by default the *largest* component) to suppress automorphisms that
|
|
22
|
+
swap isomorphic components. We then compute automorphisms **within each
|
|
23
|
+
component** independently and combine component orbits. The total number of
|
|
24
|
+
automorphisms is the product of component automorphism counts (excluding
|
|
25
|
+
component-permutation symmetries by design due to anchoring).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from collections import defaultdict
|
|
31
|
+
from typing import (
|
|
32
|
+
Dict,
|
|
33
|
+
Iterable,
|
|
34
|
+
List,
|
|
35
|
+
Mapping,
|
|
36
|
+
Optional,
|
|
37
|
+
Sequence,
|
|
38
|
+
Tuple,
|
|
39
|
+
Union,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
import networkx as nx
|
|
43
|
+
from networkx.algorithms.isomorphism import (
|
|
44
|
+
GraphMatcher,
|
|
45
|
+
categorical_edge_match,
|
|
46
|
+
categorical_multiedge_match,
|
|
47
|
+
categorical_node_match,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Typing aliases
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
NodeId = Union[int, str, Tuple, object]
|
|
54
|
+
MappingDict = Mapping[NodeId, NodeId]
|
|
55
|
+
|
|
56
|
+
__all__ = ["Automorphism", "NodeId", "MappingDict"]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Automorphism:
|
|
60
|
+
"""Analyze the automorphism group of a graph and prune sub-graph mappings
|
|
61
|
+
that are equivalent under those symmetries.
|
|
62
|
+
|
|
63
|
+
Two nodes are in the same orbit if there exists an automorphism
|
|
64
|
+
:math:`\\sigma` such that :math:`\\sigma(u) = v`.
|
|
65
|
+
|
|
66
|
+
:param graph: The host graph for which to compute automorphisms.
|
|
67
|
+
:type graph: nx.Graph
|
|
68
|
+
:param node_attr_keys: Sequence of node attribute keys to respect in the automorphism
|
|
69
|
+
computation (i.e., nodes must match on these attributes). Defaults to
|
|
70
|
+
``("element", "charge")``.
|
|
71
|
+
:type node_attr_keys: Sequence[str] | None, optional
|
|
72
|
+
:param edge_attr_keys: Sequence of edge attribute keys to respect in the automorphism
|
|
73
|
+
computation. Defaults to ``("order",)``.
|
|
74
|
+
:type edge_attr_keys: Sequence[str] | None, optional
|
|
75
|
+
:param anchor_largest_component: If ``True`` and the graph is disconnected, chooses the largest connected
|
|
76
|
+
component as an "anchor" to suppress automorphisms that swap isomorphic
|
|
77
|
+
components. Defaults to ``True``.
|
|
78
|
+
:type anchor_largest_component: bool, optional
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
_DEF_NODE_ATTRS: Tuple[str, ...] = ("element", "charge")
|
|
82
|
+
_DEF_EDGE_ATTRS: Tuple[str, ...] = ("order",)
|
|
83
|
+
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
graph: nx.Graph,
|
|
87
|
+
node_attr_keys: Optional[Sequence[str]] = None,
|
|
88
|
+
edge_attr_keys: Optional[Sequence[str]] = None,
|
|
89
|
+
*,
|
|
90
|
+
anchor_largest_component: bool = True,
|
|
91
|
+
) -> None:
|
|
92
|
+
self._graph: nx.Graph = graph
|
|
93
|
+
self._nkeys: Tuple[str, ...] = (
|
|
94
|
+
tuple(node_attr_keys)
|
|
95
|
+
if node_attr_keys is not None
|
|
96
|
+
else self._DEF_NODE_ATTRS
|
|
97
|
+
)
|
|
98
|
+
self._ekeys: Tuple[str, ...] = (
|
|
99
|
+
tuple(edge_attr_keys)
|
|
100
|
+
if edge_attr_keys is not None
|
|
101
|
+
else self._DEF_EDGE_ATTRS
|
|
102
|
+
)
|
|
103
|
+
self._anchor_largest: bool = bool(anchor_largest_component)
|
|
104
|
+
|
|
105
|
+
self._orbits: Optional[List[frozenset[NodeId]]] = None
|
|
106
|
+
self._n_automorphisms: Optional[int] = None
|
|
107
|
+
self._orbit_index: Optional[Dict[NodeId, int]] = None
|
|
108
|
+
self._components: Optional[List[frozenset[NodeId]]] = None
|
|
109
|
+
self._anchor_component: Optional[frozenset[NodeId]] = None
|
|
110
|
+
|
|
111
|
+
# ------------------------------------------------------------------
|
|
112
|
+
# Public API
|
|
113
|
+
# ------------------------------------------------------------------
|
|
114
|
+
@property
|
|
115
|
+
def orbits(self) -> List[frozenset[NodeId]]:
|
|
116
|
+
"""Node-orbits of the graph under its automorphism group.
|
|
117
|
+
|
|
118
|
+
:return: Orbits, computed lazily and cached.
|
|
119
|
+
:rtype: list[frozenset[NodeId]]
|
|
120
|
+
"""
|
|
121
|
+
if self._orbits is None:
|
|
122
|
+
self._analyze()
|
|
123
|
+
return self._orbits # type: ignore[return-value]
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def n_automorphisms(self) -> int:
|
|
127
|
+
"""Number of automorphisms of the host graph.
|
|
128
|
+
|
|
129
|
+
:return: Group order (at least 1).
|
|
130
|
+
:rtype: int
|
|
131
|
+
"""
|
|
132
|
+
if self._n_automorphisms is None:
|
|
133
|
+
self._analyze()
|
|
134
|
+
return int(self._n_automorphisms) # type: ignore[return-value]
|
|
135
|
+
|
|
136
|
+
# ------------------------------------------------------------------
|
|
137
|
+
# Connected / disconnected handling
|
|
138
|
+
# ------------------------------------------------------------------
|
|
139
|
+
@property
|
|
140
|
+
def is_connected(self) -> bool:
|
|
141
|
+
"""Whether the host graph is connected (weakly for directed graphs).
|
|
142
|
+
|
|
143
|
+
:return: ``True`` if connected or has 0/1 nodes, else ``False``.
|
|
144
|
+
:rtype: bool
|
|
145
|
+
"""
|
|
146
|
+
n = self._graph.number_of_nodes()
|
|
147
|
+
if n <= 1:
|
|
148
|
+
return True
|
|
149
|
+
return len(self.components) == 1
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def components(self) -> List[frozenset[NodeId]]:
|
|
153
|
+
"""Connected components (weakly for directed graphs).
|
|
154
|
+
|
|
155
|
+
:return: Components as frozensets of node IDs.
|
|
156
|
+
:rtype: list[frozenset[NodeId]]
|
|
157
|
+
"""
|
|
158
|
+
if self._components is None:
|
|
159
|
+
self._components = self._compute_components()
|
|
160
|
+
return self._components
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def anchor_component(self) -> Optional[frozenset[NodeId]]:
|
|
164
|
+
"""Anchor component used for disconnected graphs.
|
|
165
|
+
|
|
166
|
+
:return: Anchor component node-set, or ``None`` if graph is connected.
|
|
167
|
+
:rtype: frozenset[NodeId] | None
|
|
168
|
+
"""
|
|
169
|
+
if self._orbits is None:
|
|
170
|
+
self._analyze()
|
|
171
|
+
return self._anchor_component
|
|
172
|
+
|
|
173
|
+
# ------------------------------------------------------------------
|
|
174
|
+
# Internal helpers
|
|
175
|
+
# ------------------------------------------------------------------
|
|
176
|
+
def _compute_components(self) -> List[frozenset[NodeId]]:
|
|
177
|
+
if self._graph.number_of_nodes() == 0:
|
|
178
|
+
return []
|
|
179
|
+
if self._graph.is_directed():
|
|
180
|
+
comps: Iterable[Iterable[NodeId]] = nx.weakly_connected_components(self._graph) # type: ignore[arg-type]
|
|
181
|
+
else:
|
|
182
|
+
comps = nx.connected_components(self._graph) # type: ignore[arg-type]
|
|
183
|
+
return [frozenset(c) for c in comps]
|
|
184
|
+
|
|
185
|
+
def _node_defaults(self) -> List[object]:
|
|
186
|
+
defaults: List[object] = []
|
|
187
|
+
for k in self._nkeys:
|
|
188
|
+
defaults.append(0 if k == "charge" else "*")
|
|
189
|
+
return defaults
|
|
190
|
+
|
|
191
|
+
def _edge_defaults(self) -> List[object]:
|
|
192
|
+
return [1.0 for _ in self._ekeys]
|
|
193
|
+
|
|
194
|
+
def _make_matcher(self, g: nx.Graph) -> GraphMatcher:
|
|
195
|
+
if g.is_multigraph():
|
|
196
|
+
matcher_cls = (
|
|
197
|
+
nx.algorithms.isomorphism.MultiDiGraphMatcher
|
|
198
|
+
if g.is_directed()
|
|
199
|
+
else nx.algorithms.isomorphism.MultiGraphMatcher
|
|
200
|
+
)
|
|
201
|
+
edge_match = categorical_multiedge_match(
|
|
202
|
+
self._ekeys,
|
|
203
|
+
self._edge_defaults(),
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
matcher_cls = (
|
|
207
|
+
nx.algorithms.isomorphism.DiGraphMatcher
|
|
208
|
+
if g.is_directed()
|
|
209
|
+
else GraphMatcher
|
|
210
|
+
)
|
|
211
|
+
edge_match = categorical_edge_match(
|
|
212
|
+
self._ekeys,
|
|
213
|
+
self._edge_defaults(),
|
|
214
|
+
)
|
|
215
|
+
return matcher_cls(
|
|
216
|
+
g,
|
|
217
|
+
g,
|
|
218
|
+
node_match=categorical_node_match(self._nkeys, self._node_defaults()),
|
|
219
|
+
edge_match=edge_match,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def _analyze_component(self, g: nx.Graph) -> Tuple[List[frozenset[NodeId]], int]:
|
|
223
|
+
if g.number_of_nodes() == 0:
|
|
224
|
+
return ([], 1)
|
|
225
|
+
if g.number_of_nodes() == 1:
|
|
226
|
+
node = next(iter(g.nodes))
|
|
227
|
+
return ([frozenset({node})], 1)
|
|
228
|
+
|
|
229
|
+
gm = self._make_matcher(g)
|
|
230
|
+
|
|
231
|
+
orbit_sets: Dict[NodeId, set[NodeId]] = defaultdict(set)
|
|
232
|
+
n_aut = 0
|
|
233
|
+
|
|
234
|
+
for auto in gm.isomorphisms_iter():
|
|
235
|
+
n_aut += 1
|
|
236
|
+
for u, v in auto.items():
|
|
237
|
+
orbit_sets[u].add(v)
|
|
238
|
+
orbit_sets[v].add(u)
|
|
239
|
+
|
|
240
|
+
if not orbit_sets:
|
|
241
|
+
return ([frozenset({n}) for n in g.nodes], 1)
|
|
242
|
+
|
|
243
|
+
unique_orbits = {frozenset(nodes) for nodes in orbit_sets.values()}
|
|
244
|
+
return (list(unique_orbits), n_aut if n_aut > 0 else 1)
|
|
245
|
+
|
|
246
|
+
def _choose_anchor(
|
|
247
|
+
self, comps: List[frozenset[NodeId]]
|
|
248
|
+
) -> Optional[frozenset[NodeId]]:
|
|
249
|
+
if len(comps) <= 1 or not self._anchor_largest:
|
|
250
|
+
return None
|
|
251
|
+
return max(comps, key=len)
|
|
252
|
+
|
|
253
|
+
def _sorted_orbits(
|
|
254
|
+
self, orbits: List[frozenset[NodeId]]
|
|
255
|
+
) -> List[frozenset[NodeId]]:
|
|
256
|
+
def _key(o: frozenset[NodeId]) -> Tuple[int, str]:
|
|
257
|
+
return (len(o), "|".join(sorted((repr(x) for x in o))))
|
|
258
|
+
|
|
259
|
+
return sorted(orbits, key=_key)
|
|
260
|
+
|
|
261
|
+
def _analyze(self) -> None:
|
|
262
|
+
comps = self.components
|
|
263
|
+
|
|
264
|
+
if self._graph.number_of_nodes() == 0:
|
|
265
|
+
self._orbits = []
|
|
266
|
+
self._n_automorphisms = 1
|
|
267
|
+
self._anchor_component = None
|
|
268
|
+
return
|
|
269
|
+
|
|
270
|
+
if len(comps) <= 1:
|
|
271
|
+
orbits, n_aut = self._analyze_component(self._graph)
|
|
272
|
+
self._orbits = self._sorted_orbits(orbits)
|
|
273
|
+
self._n_automorphisms = n_aut
|
|
274
|
+
self._anchor_component = None
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
# Disconnected: anchor largest component; compute per-component autos
|
|
278
|
+
self._anchor_component = self._choose_anchor(comps)
|
|
279
|
+
|
|
280
|
+
all_orbits: List[frozenset[NodeId]] = []
|
|
281
|
+
total_aut = 1
|
|
282
|
+
|
|
283
|
+
for comp in comps:
|
|
284
|
+
sub = self._graph.subgraph(comp).copy()
|
|
285
|
+
orbits, n_aut = self._analyze_component(sub)
|
|
286
|
+
all_orbits.extend(orbits)
|
|
287
|
+
total_aut *= int(n_aut)
|
|
288
|
+
|
|
289
|
+
self._orbits = self._sorted_orbits(list({frozenset(o) for o in all_orbits}))
|
|
290
|
+
self._n_automorphisms = int(total_aut) if total_aut > 0 else 1
|
|
291
|
+
|
|
292
|
+
def _get_orbit_index(self) -> Dict[NodeId, int]:
|
|
293
|
+
if self._orbit_index is None:
|
|
294
|
+
self._orbit_index = {
|
|
295
|
+
node: idx for idx, orb in enumerate(self.orbits) for node in orb
|
|
296
|
+
}
|
|
297
|
+
return self._orbit_index
|
|
298
|
+
|
|
299
|
+
# ------------------------------------------------------------------
|
|
300
|
+
# Dunder methods
|
|
301
|
+
# ------------------------------------------------------------------
|
|
302
|
+
def __len__(self) -> int:
|
|
303
|
+
return len(self.orbits)
|
|
304
|
+
|
|
305
|
+
def __repr__(self) -> str:
|
|
306
|
+
n_nodes = self._graph.number_of_nodes()
|
|
307
|
+
conn = "connected" if self.is_connected else "disconnected"
|
|
308
|
+
n_comp = len(self.components)
|
|
309
|
+
n_orb = len(self.orbits) if self._orbits is not None else "?"
|
|
310
|
+
n_aut = self._n_automorphisms if self._n_automorphisms is not None else "?"
|
|
311
|
+
return (
|
|
312
|
+
f"<Automorphism | {conn} nodes={n_nodes} comps={n_comp} "
|
|
313
|
+
f"orbits={n_orb} automorphisms={n_aut}>"
|
|
314
|
+
)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from operator import eq
|
|
2
|
+
from typing import List, Dict, Any, Tuple, Optional, Callable
|
|
3
|
+
from networkx.algorithms.isomorphism import generic_node_match, generic_edge_match
|
|
4
|
+
from synkit.Utils.utils import stratified_random_sample
|
|
5
|
+
from synkit.Rule.Modify.rule_utils import strip_context
|
|
6
|
+
from synkit.Graph.Matcher.graph_cluster import GraphCluster, _as_native_graph
|
|
7
|
+
from synkit.Graph.Matcher.graph_morphism import graph_isomorphism
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BatchCluster:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
node_label_names: List[str] = ["element", "charge"],
|
|
14
|
+
node_label_default: List[Any] = ["*", 0],
|
|
15
|
+
edge_attribute: str = "order",
|
|
16
|
+
backend: str = "nx",
|
|
17
|
+
):
|
|
18
|
+
"""Initializes an AutoCat instance which uses isomorphism checks for
|
|
19
|
+
categorizing new graphs or rules.
|
|
20
|
+
|
|
21
|
+
:param node_label_names: Names of the node attributes to use in
|
|
22
|
+
isomorphism checks.
|
|
23
|
+
:type node_label_names: List[str]
|
|
24
|
+
:param node_label_default: Default values for node attributes if they are
|
|
25
|
+
missing in the graph data.
|
|
26
|
+
:type node_label_default: List[Any]
|
|
27
|
+
:param edge_attribute: The edge attribute to consider when checking isomorphism
|
|
28
|
+
between graphs.
|
|
29
|
+
:type edge_attribute: str
|
|
30
|
+
|
|
31
|
+
:raises ValueError: If the lengths of `node_label_names` and `node_label_default` do not match.
|
|
32
|
+
"""
|
|
33
|
+
self.backend = backend.lower()
|
|
34
|
+
if self.backend != "nx":
|
|
35
|
+
raise ValueError(f"Unsupported backend: {backend!r}")
|
|
36
|
+
if len(node_label_names) != len(node_label_default):
|
|
37
|
+
raise ValueError(
|
|
38
|
+
"The lengths of `node_label_names` and `node_label_default` must match."
|
|
39
|
+
)
|
|
40
|
+
if backend == "nx":
|
|
41
|
+
self.nodeLabelNames = node_label_names
|
|
42
|
+
self.nodeLabelDefault = node_label_default
|
|
43
|
+
self.edgeAttribute = edge_attribute
|
|
44
|
+
self.nodeMatch = generic_node_match(
|
|
45
|
+
self.nodeLabelNames, self.nodeLabelDefault, [eq] * len(node_label_names)
|
|
46
|
+
)
|
|
47
|
+
self.edgeMatch = generic_edge_match(edge_attribute, 1, eq)
|
|
48
|
+
|
|
49
|
+
def available_backends(self) -> List[str]:
|
|
50
|
+
"""Return the native matching backend."""
|
|
51
|
+
return ["nx"]
|
|
52
|
+
|
|
53
|
+
def lib_check(
|
|
54
|
+
self,
|
|
55
|
+
data: Dict,
|
|
56
|
+
templates: List[Dict],
|
|
57
|
+
rule_key: str = "gml",
|
|
58
|
+
attribute_key: str = "signature",
|
|
59
|
+
nodeMatch: Optional[Callable] = None,
|
|
60
|
+
edgeMatch: Optional[Callable] = None,
|
|
61
|
+
) -> Dict:
|
|
62
|
+
"""Checks and classifies a graph or rule based on existing templates
|
|
63
|
+
using either graph or rule isomorphism.
|
|
64
|
+
|
|
65
|
+
:param data: A dictionary representing a graph or rule with its attributes and
|
|
66
|
+
classification.
|
|
67
|
+
:type data: Dict
|
|
68
|
+
:param templates: Dynamic templates used for categorization. If None, initializes to an empty list.
|
|
69
|
+
:type templates: List[Dict]
|
|
70
|
+
:param rule_key: Key to access the graph or rule data within the dictionary.
|
|
71
|
+
:type rule_key: str
|
|
72
|
+
:param attribute_key: An attribute used to filter templates before isomorphism check.
|
|
73
|
+
:type attribute_key: str
|
|
74
|
+
:param nodeMatch: A function to match nodes, defaults to a predefined generic_node_match.
|
|
75
|
+
:type nodeMatch: Optional[Callable]
|
|
76
|
+
:param edgeMatch: A function to match edges, defaults to a predefined generic_edge_match.
|
|
77
|
+
:type edgeMatch: Optional[Callable]
|
|
78
|
+
|
|
79
|
+
:return: The updated dictionary with its classification.
|
|
80
|
+
:rtype: Dict
|
|
81
|
+
"""
|
|
82
|
+
# Ensure that templates are not None
|
|
83
|
+
if templates is None:
|
|
84
|
+
templates = []
|
|
85
|
+
|
|
86
|
+
att = data.get(attribute_key)
|
|
87
|
+
sub_temp = [temp for temp in templates if temp.get(attribute_key) == att]
|
|
88
|
+
|
|
89
|
+
for template in sub_temp:
|
|
90
|
+
template_data = (
|
|
91
|
+
strip_context(template[rule_key])
|
|
92
|
+
if isinstance(template[rule_key], str)
|
|
93
|
+
else template[rule_key]
|
|
94
|
+
)
|
|
95
|
+
data_rule = (
|
|
96
|
+
strip_context(data[rule_key])
|
|
97
|
+
if isinstance(data[rule_key], str)
|
|
98
|
+
else data[rule_key]
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if graph_isomorphism(
|
|
102
|
+
_as_native_graph(template_data),
|
|
103
|
+
_as_native_graph(data_rule),
|
|
104
|
+
nodeMatch or self.nodeMatch,
|
|
105
|
+
edgeMatch or self.edgeMatch,
|
|
106
|
+
):
|
|
107
|
+
data["class"] = template["class"]
|
|
108
|
+
break
|
|
109
|
+
else:
|
|
110
|
+
new_class = max((temp["class"] for temp in templates), default=-1) + 1
|
|
111
|
+
data["class"] = new_class
|
|
112
|
+
templates.append(data.copy()) # Append a copy to avoid reference issues
|
|
113
|
+
|
|
114
|
+
return data, templates
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def batch_dicts(input_list, batch_size):
|
|
118
|
+
"""Splits a list of dictionaries into batches of a specified size.
|
|
119
|
+
|
|
120
|
+
:param input_list: The list of dictionaries to be batched.
|
|
121
|
+
:type input_list: list of dict
|
|
122
|
+
:param batch_size: The size of each batch.
|
|
123
|
+
:type batch_size: int
|
|
124
|
+
|
|
125
|
+
:return: A list where each element is a batch (sublist) of dictionaries.
|
|
126
|
+
:rtype: list of list of dict
|
|
127
|
+
|
|
128
|
+
:raises ValueError: If batch_size is less than 1.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
# Validate batch_size to ensure it's a positive integer
|
|
132
|
+
if batch_size < 1:
|
|
133
|
+
raise ValueError("batch_size must be at least 1")
|
|
134
|
+
|
|
135
|
+
# Initialize an empty list to hold the batches
|
|
136
|
+
batches = []
|
|
137
|
+
|
|
138
|
+
# Iterate over the input list in steps of batch_size
|
|
139
|
+
for i in range(0, len(input_list), batch_size):
|
|
140
|
+
# Append a batch slice to the batches list
|
|
141
|
+
# fmt: off
|
|
142
|
+
batches.append(input_list[i: i + batch_size])
|
|
143
|
+
# fmt: on
|
|
144
|
+
|
|
145
|
+
return batches
|
|
146
|
+
|
|
147
|
+
def cluster(
|
|
148
|
+
self,
|
|
149
|
+
data: List[Dict],
|
|
150
|
+
templates: List[Dict],
|
|
151
|
+
rule_key: str = "gml",
|
|
152
|
+
attribute_key: str = "WLHash",
|
|
153
|
+
) -> Tuple[List[Dict], List[Dict]]:
|
|
154
|
+
"""Processes a list of graph data entries, classifying each based on
|
|
155
|
+
existing templates.
|
|
156
|
+
|
|
157
|
+
:param data: A list of dictionaries, each representing a graph or rule
|
|
158
|
+
to be classified.
|
|
159
|
+
:type data: List[Dict]
|
|
160
|
+
:param templates: Dynamic templates used for categorization.
|
|
161
|
+
:type templates: List[Dict]
|
|
162
|
+
|
|
163
|
+
:return: A tuple containing the list of classified data and the updated templates.
|
|
164
|
+
:rtype: Tuple[List[Dict], List[Dict]]
|
|
165
|
+
"""
|
|
166
|
+
for entry in data:
|
|
167
|
+
_, templates = self.lib_check(entry, templates, rule_key, attribute_key)
|
|
168
|
+
return data, templates
|
|
169
|
+
|
|
170
|
+
def fit(
|
|
171
|
+
self,
|
|
172
|
+
data: List[Dict],
|
|
173
|
+
templates: List[Dict],
|
|
174
|
+
rule_key: str = "gml",
|
|
175
|
+
attribute_key: str = "WLHash",
|
|
176
|
+
batch_size: Optional[int] = None,
|
|
177
|
+
) -> Tuple[List[Dict], List[Dict]]:
|
|
178
|
+
"""Processes and classifies data in batches. Uses GraphCluster for
|
|
179
|
+
initial processing and a stratified sampling technique to update
|
|
180
|
+
templates if there is only one batch and no initial templates are
|
|
181
|
+
provided.
|
|
182
|
+
|
|
183
|
+
:param data: Data to process.
|
|
184
|
+
:type data: List[Dict]
|
|
185
|
+
:param templates: Templates for categorization.
|
|
186
|
+
:type templates: List[Dict]
|
|
187
|
+
:param rule_key: Key to access rule or graph data.
|
|
188
|
+
:type rule_key: str
|
|
189
|
+
:param attribute_key: Key to access attributes used for filtering.
|
|
190
|
+
:type attribute_key: str
|
|
191
|
+
:param batch_size: Size of batches for processing, if not provided, processes all data at once.
|
|
192
|
+
:type batch_size: Optional[int]
|
|
193
|
+
|
|
194
|
+
:return: The processed data and the potentially updated templates.
|
|
195
|
+
:rtype: Tuple[List[Dict], List[Dict]]
|
|
196
|
+
"""
|
|
197
|
+
if batch_size is not None:
|
|
198
|
+
batches = self.batch_dicts(data, batch_size)
|
|
199
|
+
else:
|
|
200
|
+
batches = [data] # Process all at once if no batch size provided
|
|
201
|
+
|
|
202
|
+
output_data, output_templates = [], templates if templates is not None else []
|
|
203
|
+
graph_cluster = GraphCluster()
|
|
204
|
+
|
|
205
|
+
if len(batches) == 1:
|
|
206
|
+
batch = batches[0]
|
|
207
|
+
if not templates:
|
|
208
|
+
output_data = graph_cluster.fit(batch, rule_key, attribute_key)
|
|
209
|
+
output_templates = stratified_random_sample(
|
|
210
|
+
output_data, property_key="class", samples_per_class=1, seed=1
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
output_data, output_templates = self.cluster(
|
|
214
|
+
batch, output_templates, rule_key, attribute_key
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
for batch in batches:
|
|
218
|
+
processed_data, new_templates = self.cluster(
|
|
219
|
+
batch, output_templates, rule_key, attribute_key
|
|
220
|
+
)
|
|
221
|
+
output_data.extend(processed_data)
|
|
222
|
+
output_templates = new_templates
|
|
223
|
+
|
|
224
|
+
return output_data, output_templates
|