okstra 0.114.0 → 0.116.0
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.
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/follow-ups/2026-07-10-final-report-option-3.md +51 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +1 -0
- package/runtime/prompts/launch.template.md +34 -0
- package/runtime/prompts/lead/convergence.md +2 -0
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +1 -0
- package/runtime/python/okstra_ctl/implementation_stage.py +46 -39
- package/runtime/python/okstra_ctl/incremental_carry.py +70 -0
- package/runtime/python/okstra_ctl/incremental_scope.py +89 -0
- package/runtime/python/okstra_ctl/report_views.py +151 -12
- package/runtime/python/okstra_ctl/run.py +24 -24
- package/runtime/python/okstra_ctl/stage_targets.py +26 -0
- package/runtime/python/okstra_ctl/worktree.py +1 -1
- package/runtime/python/okstra_token_usage/claude.py +58 -19
- package/runtime/python/okstra_token_usage/collect.py +3 -1
- package/runtime/python/okstra_vendor/__init__.py +3 -0
- package/runtime/schemas/final-report-v1.0.schema.json +39 -0
- package/runtime/templates/reports/final-report.template.md +27 -0
- package/runtime/templates/reports/i18n/en.json +23 -12
- package/runtime/templates/reports/i18n/ko.json +23 -12
- package/runtime/templates/reports/report.css +55 -0
- package/runtime/templates/reports/report.js +53 -1
- package/runtime/validators/validate-run.py +50 -0
- package/src/cli-registry.mjs +14 -0
- package/src/commands/execute/incremental-carry.mjs +20 -0
- package/src/commands/execute/incremental-scope.mjs +20 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +0 -41
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_clique.py +0 -112
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_connectivity.py +0 -199
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_density.py +0 -146
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_distance_measures.py +0 -59
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_dominating_set.py +0 -78
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_kcomponents.py +0 -303
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_matching.py +0 -8
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_maxcut.py +0 -94
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_ramsey.py +0 -31
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_steinertree.py +0 -306
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_traveling_salesman.py +0 -1014
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_treewidth.py +0 -274
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_vertex_cover.py +0 -68
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/base_test.py +0 -81
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_connectivity.py +0 -143
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_correlation.py +0 -122
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_mixing.py +0 -174
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_neighbor_degree.py +0 -107
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_pairs.py +0 -87
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_basic.py +0 -125
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_centrality.py +0 -192
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_cluster.py +0 -84
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_covering.py +0 -33
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_edgelist.py +0 -240
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_extendability.py +0 -334
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_generators.py +0 -407
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_link_analysis.py +0 -218
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matching.py +0 -327
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matrix.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_project.py +0 -409
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_redundancy.py +0 -35
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py +0 -80
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality.py +0 -923
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py +0 -354
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_closeness_centrality.py +0 -274
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py +0 -259
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py +0 -147
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_closeness.py +0 -43
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_degree_centrality.py +0 -144
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_dispersion.py +0 -73
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py +0 -186
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_group.py +0 -277
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_harmonic_centrality.py +0 -122
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_katz_centrality.py +0 -345
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_laplacian_centrality.py +0 -220
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_load_centrality.py +0 -344
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_percolation_centrality.py +0 -87
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_reaching.py +0 -140
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_second_order_centrality.py +0 -82
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_subgraph.py +0 -110
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_trophic.py +0 -302
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_voterank.py +0 -64
- package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/test_coloring.py +0 -863
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_asyn_fluid.py +0 -147
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_bipartitions.py +0 -157
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_centrality.py +0 -85
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_divisive.py +0 -106
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_kclique.py +0 -91
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_label_propagation.py +0 -241
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_leiden.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_local.py +0 -76
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_louvain.py +0 -264
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_lukes.py +0 -152
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_modularity_max.py +0 -340
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_quality.py +0 -139
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_utils.py +0 -26
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_attracting.py +0 -70
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_biconnected.py +0 -248
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_connected.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_semiconnected.py +0 -55
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_strongly_connected.py +0 -193
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_weakly_connected.py +0 -96
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_connectivity.py +0 -421
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_cuts.py +0 -309
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_disjoint_paths.py +0 -249
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +0 -502
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +0 -488
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcomponents.py +0 -323
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcutsets.py +0 -280
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_stoer_wagner.py +0 -102
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gl1.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gw1.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/netgen-2.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_gomory_hu.py +0 -128
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow.py +0 -573
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +0 -155
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_mincost.py +0 -475
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_networksimplex.py +0 -481
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/wlm3.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.A99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.B99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.A99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.B99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_ismags.py +0 -719
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphism.py +0 -103
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphvf2.py +0 -490
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_match_helpers.py +0 -64
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py +0 -212
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py +0 -202
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp.py +0 -1655
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py +0 -3118
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2userfunc.py +0 -196
- package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_hits.py +0 -77
- package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_pagerank.py +0 -213
- package/runtime/python/okstra_vendor/networkx/algorithms/minors/tests/test_contraction.py +0 -544
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_all.py +0 -328
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_binary.py +0 -451
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_product.py +0 -491
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_unary.py +0 -55
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_astar.py +0 -254
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense.py +0 -212
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +0 -88
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_generic.py +0 -511
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_unweighted.py +0 -149
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_weighted.py +0 -983
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_asteroidal.py +0 -23
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_boundary.py +0 -154
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_bridges.py +0 -144
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_broadcasting.py +0 -109
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chains.py +0 -136
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chordal.py +0 -129
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_clique.py +0 -300
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cluster.py +0 -678
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_communicability.py +0 -80
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_core.py +0 -266
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_covering.py +0 -85
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cuts.py +0 -171
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cycles.py +0 -984
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_d_separation.py +0 -340
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dag.py +0 -835
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_measures.py +0 -831
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_regular.py +0 -85
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominance.py +0 -299
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominating.py +0 -115
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_efficiency.py +0 -58
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_euler.py +0 -314
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graph_hashing.py +0 -872
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graphical.py +0 -163
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hierarchy.py +0 -46
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hybrid.py +0 -24
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_isolate.py +0 -26
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_link_prediction.py +0 -615
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_lowest_common_ancestors.py +0 -459
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_matching.py +0 -556
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_max_weight_clique.py +0 -179
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_mis.py +0 -62
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_moral.py +0 -15
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_node_classification.py +0 -140
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_non_randomness.py +0 -60
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_perfect_graph.py +0 -27
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planar_drawing.py +0 -274
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planarity.py +0 -556
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_polynomials.py +0 -57
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_reciprocity.py +0 -37
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_regular.py +0 -88
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_richclub.py +0 -149
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_similarity.py +0 -1158
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_simple_paths.py +0 -803
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smallworld.py +0 -76
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smetric.py +0 -8
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_sparsifiers.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_structuralholes.py +0 -191
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_summarization.py +0 -642
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_swap.py +0 -179
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_threshold.py +0 -270
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_time_dependent.py +0 -431
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_tournament.py +0 -161
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_triads.py +0 -248
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_vitality.py +0 -41
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_voronoi.py +0 -103
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_walks.py +0 -54
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_wiener.py +0 -157
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_beamsearch.py +0 -25
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_bfs.py +0 -203
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_dfs.py +0 -307
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgebfs.py +0 -147
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgedfs.py +0 -131
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_branchings.py +0 -624
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_coding.py +0 -114
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_decomposition.py +0 -79
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_distance_measures.py +0 -99
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_mst.py +0 -934
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_operations.py +0 -53
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_recognition.py +0 -174
- package/runtime/python/okstra_vendor/networkx/classes/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/classes/tests/dispatch_interface.py +0 -192
- package/runtime/python/okstra_vendor/networkx/classes/tests/historical_tests.py +0 -476
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_coreviews.py +0 -362
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph.py +0 -331
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph_historical.py +0 -110
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_filters.py +0 -177
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_function.py +0 -1045
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph.py +0 -950
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph_historical.py +0 -12
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_graphviews.py +0 -349
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_multidigraph.py +0 -459
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_multigraph.py +0 -528
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_reportviews.py +0 -1421
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_special.py +0 -131
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_subgraphviews.py +0 -371
- package/runtime/python/okstra_vendor/networkx/conftest.py +0 -261
- package/runtime/python/okstra_vendor/networkx/drawing/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_complex.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_empty_graph.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_house_with_colors.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_labels_and_colors.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_shortest_path.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_house_with_colors.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_agraph.py +0 -237
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_image_comparison_pylab_mpl.py +0 -229
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_latex.py +0 -285
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_layout.py +0 -631
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pydot.py +0 -146
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pylab.py +0 -1582
- package/runtime/python/okstra_vendor/networkx/generators/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_atlas.py +0 -75
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_classic.py +0 -642
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_cographs.py +0 -20
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_community.py +0 -362
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_degree_seq.py +0 -224
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_directed.py +0 -189
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_duplication.py +0 -103
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_ego.py +0 -39
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_expanders.py +0 -182
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_geometric.py +0 -488
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_harary_graph.py +0 -133
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_internet_as_graphs.py +0 -221
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_intersection.py +0 -28
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_interval_graph.py +0 -144
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_joint_degree_seq.py +0 -125
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_lattice.py +0 -264
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_line.py +0 -316
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_mycielski.py +0 -30
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_nonisomorphic_trees.py +0 -82
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_clustered.py +0 -33
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_graphs.py +0 -495
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_small.py +0 -220
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_spectral_graph_forge.py +0 -49
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_stochastic.py +0 -72
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_sudoku.py +0 -92
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_time_series.py +0 -64
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_trees.py +0 -195
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_triads.py +0 -15
- package/runtime/python/okstra_vendor/networkx/linalg/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_algebraic_connectivity.py +0 -400
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_attrmatrix.py +0 -108
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_bethehessian.py +0 -40
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_graphmatrix.py +0 -275
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_laplacian.py +0 -334
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_modularity.py +0 -86
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_spectrum.py +0 -70
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_adjacency.py +0 -78
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_cytoscape.py +0 -78
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_node_link.py +0 -109
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_tree.py +0 -48
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_adjlist.py +0 -354
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_edgelist.py +0 -318
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gexf.py +0 -612
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gml.py +0 -744
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graph6.py +0 -181
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graphml.py +0 -1531
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_leda.py +0 -30
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_p2g.py +0 -63
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_pajek.py +0 -128
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_sparse6.py +0 -166
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_text.py +0 -1742
- package/runtime/python/okstra_vendor/networkx/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/tests/test_all_random_functions.py +0 -248
- package/runtime/python/okstra_vendor/networkx/tests/test_convert.py +0 -321
- package/runtime/python/okstra_vendor/networkx/tests/test_convert_numpy.py +0 -531
- package/runtime/python/okstra_vendor/networkx/tests/test_convert_pandas.py +0 -349
- package/runtime/python/okstra_vendor/networkx/tests/test_convert_scipy.py +0 -281
- package/runtime/python/okstra_vendor/networkx/tests/test_exceptions.py +0 -40
- package/runtime/python/okstra_vendor/networkx/tests/test_import.py +0 -11
- package/runtime/python/okstra_vendor/networkx/tests/test_lazy_imports.py +0 -96
- package/runtime/python/okstra_vendor/networkx/tests/test_relabel.py +0 -349
- package/runtime/python/okstra_vendor/networkx/tests/test_removed_functions_exception_messages.py +0 -8
- package/runtime/python/okstra_vendor/networkx/utils/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/utils/tests/test__init.py +0 -11
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_backends.py +0 -225
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_config.py +0 -263
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_decorators.py +0 -510
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_heaps.py +0 -131
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_mapped_queue.py +0 -268
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_misc.py +0 -393
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_random_sequence.py +0 -53
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_rcm.py +0 -63
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_unionfind.py +0 -55
|
@@ -1,1158 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
import networkx as nx
|
|
4
|
-
from networkx.algorithms.similarity import (
|
|
5
|
-
graph_edit_distance,
|
|
6
|
-
optimal_edit_paths,
|
|
7
|
-
optimize_graph_edit_distance,
|
|
8
|
-
)
|
|
9
|
-
from networkx.generators.classic import (
|
|
10
|
-
circular_ladder_graph,
|
|
11
|
-
cycle_graph,
|
|
12
|
-
path_graph,
|
|
13
|
-
wheel_graph,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@pytest.mark.parametrize("source", (10, "foo"))
|
|
18
|
-
def test_generate_random_paths_source_not_in_G(source):
|
|
19
|
-
pytest.importorskip("numpy")
|
|
20
|
-
G = nx.complete_graph(5)
|
|
21
|
-
# No exception at generator construction time
|
|
22
|
-
path_gen = nx.generate_random_paths(G, sample_size=3, source=source)
|
|
23
|
-
with pytest.raises(nx.NodeNotFound, match="Initial node.*not in G"):
|
|
24
|
-
next(path_gen)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
|
|
28
|
-
def test_generate_random_paths_with_isolated_nodes():
|
|
29
|
-
pytest.importorskip("numpy")
|
|
30
|
-
G = nx.Graph()
|
|
31
|
-
G.add_nodes_from([0, 1, 2])
|
|
32
|
-
G.add_edge(0, 1)
|
|
33
|
-
|
|
34
|
-
# Connected source node
|
|
35
|
-
paths = list(nx.generate_random_paths(G, 2, path_length=2, source=0, seed=42))
|
|
36
|
-
assert len(paths) == 2
|
|
37
|
-
assert all(len(path) == 3 for path in paths)
|
|
38
|
-
assert all(path[0] == 0 for path in paths)
|
|
39
|
-
|
|
40
|
-
# Isolated source node
|
|
41
|
-
path_gen = nx.generate_random_paths(G, 2, path_length=2, source=2, seed=42)
|
|
42
|
-
with pytest.raises(ValueError, match="probabilities contain NaN"):
|
|
43
|
-
list(path_gen)
|
|
44
|
-
|
|
45
|
-
# Random source that might pick isolated node
|
|
46
|
-
path_gen = nx.generate_random_paths(G, 2, path_length=2, seed=42)
|
|
47
|
-
with pytest.raises(ValueError, match="probabilities contain NaN"):
|
|
48
|
-
list(path_gen)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def nmatch(n1, n2):
|
|
52
|
-
return n1 == n2
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def ematch(e1, e2):
|
|
56
|
-
return e1 == e2
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def getCanonical():
|
|
60
|
-
G = nx.Graph()
|
|
61
|
-
G.add_node("A", label="A")
|
|
62
|
-
G.add_node("B", label="B")
|
|
63
|
-
G.add_node("C", label="C")
|
|
64
|
-
G.add_node("D", label="D")
|
|
65
|
-
G.add_edge("A", "B", label="a-b")
|
|
66
|
-
G.add_edge("B", "C", label="b-c")
|
|
67
|
-
G.add_edge("B", "D", label="b-d")
|
|
68
|
-
return G
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class TestSimilarity:
|
|
72
|
-
@classmethod
|
|
73
|
-
def setup_class(cls):
|
|
74
|
-
global np
|
|
75
|
-
np = pytest.importorskip("numpy")
|
|
76
|
-
pytest.importorskip("scipy")
|
|
77
|
-
|
|
78
|
-
def test_graph_edit_distance_roots_and_timeout(self):
|
|
79
|
-
G0 = nx.star_graph(5)
|
|
80
|
-
G1 = G0.copy()
|
|
81
|
-
pytest.raises(ValueError, graph_edit_distance, G0, G1, roots=[2])
|
|
82
|
-
pytest.raises(ValueError, graph_edit_distance, G0, G1, roots=[2, 3, 4])
|
|
83
|
-
pytest.raises(nx.NodeNotFound, graph_edit_distance, G0, G1, roots=(9, 3))
|
|
84
|
-
pytest.raises(nx.NodeNotFound, graph_edit_distance, G0, G1, roots=(3, 9))
|
|
85
|
-
pytest.raises(nx.NodeNotFound, graph_edit_distance, G0, G1, roots=(9, 9))
|
|
86
|
-
assert graph_edit_distance(G0, G1, roots=(1, 2)) == 0
|
|
87
|
-
assert graph_edit_distance(G0, G1, roots=(0, 1)) == 8
|
|
88
|
-
assert graph_edit_distance(G0, G1, roots=(1, 2), timeout=5) == 0
|
|
89
|
-
assert graph_edit_distance(G0, G1, roots=(0, 1), timeout=5) == 8
|
|
90
|
-
assert graph_edit_distance(G0, G1, roots=(0, 1), timeout=0.0001) is None
|
|
91
|
-
# test raise on 0 timeout
|
|
92
|
-
pytest.raises(nx.NetworkXError, graph_edit_distance, G0, G1, timeout=0)
|
|
93
|
-
|
|
94
|
-
def test_graph_edit_distance(self):
|
|
95
|
-
G0 = nx.Graph()
|
|
96
|
-
G1 = path_graph(6)
|
|
97
|
-
G2 = cycle_graph(6)
|
|
98
|
-
G3 = wheel_graph(7)
|
|
99
|
-
|
|
100
|
-
assert graph_edit_distance(G0, G0) == 0
|
|
101
|
-
assert graph_edit_distance(G0, G1) == 11
|
|
102
|
-
assert graph_edit_distance(G1, G0) == 11
|
|
103
|
-
assert graph_edit_distance(G0, G2) == 12
|
|
104
|
-
assert graph_edit_distance(G2, G0) == 12
|
|
105
|
-
assert graph_edit_distance(G0, G3) == 19
|
|
106
|
-
assert graph_edit_distance(G3, G0) == 19
|
|
107
|
-
|
|
108
|
-
assert graph_edit_distance(G1, G1) == 0
|
|
109
|
-
assert graph_edit_distance(G1, G2) == 1
|
|
110
|
-
assert graph_edit_distance(G2, G1) == 1
|
|
111
|
-
assert graph_edit_distance(G1, G3) == 8
|
|
112
|
-
assert graph_edit_distance(G3, G1) == 8
|
|
113
|
-
|
|
114
|
-
assert graph_edit_distance(G2, G2) == 0
|
|
115
|
-
assert graph_edit_distance(G2, G3) == 7
|
|
116
|
-
assert graph_edit_distance(G3, G2) == 7
|
|
117
|
-
|
|
118
|
-
assert graph_edit_distance(G3, G3) == 0
|
|
119
|
-
|
|
120
|
-
def test_graph_edit_distance_node_match(self):
|
|
121
|
-
G1 = cycle_graph(5)
|
|
122
|
-
G2 = cycle_graph(5)
|
|
123
|
-
for n, attr in G1.nodes.items():
|
|
124
|
-
attr["color"] = "red" if n % 2 == 0 else "blue"
|
|
125
|
-
for n, attr in G2.nodes.items():
|
|
126
|
-
attr["color"] = "red" if n % 2 == 1 else "blue"
|
|
127
|
-
assert graph_edit_distance(G1, G2) == 0
|
|
128
|
-
assert (
|
|
129
|
-
graph_edit_distance(
|
|
130
|
-
G1, G2, node_match=lambda n1, n2: n1["color"] == n2["color"]
|
|
131
|
-
)
|
|
132
|
-
== 1
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
def test_graph_edit_distance_edge_match(self):
|
|
136
|
-
G1 = path_graph(6)
|
|
137
|
-
G2 = path_graph(6)
|
|
138
|
-
for e, attr in G1.edges.items():
|
|
139
|
-
attr["color"] = "red" if min(e) % 2 == 0 else "blue"
|
|
140
|
-
for e, attr in G2.edges.items():
|
|
141
|
-
attr["color"] = "red" if min(e) // 3 == 0 else "blue"
|
|
142
|
-
assert graph_edit_distance(G1, G2) == 0
|
|
143
|
-
assert (
|
|
144
|
-
graph_edit_distance(
|
|
145
|
-
G1, G2, edge_match=lambda e1, e2: e1["color"] == e2["color"]
|
|
146
|
-
)
|
|
147
|
-
== 2
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
def test_graph_edit_distance_node_cost(self):
|
|
151
|
-
G1 = path_graph(6)
|
|
152
|
-
G2 = path_graph(6)
|
|
153
|
-
for n, attr in G1.nodes.items():
|
|
154
|
-
attr["color"] = "red" if n % 2 == 0 else "blue"
|
|
155
|
-
for n, attr in G2.nodes.items():
|
|
156
|
-
attr["color"] = "red" if n % 2 == 1 else "blue"
|
|
157
|
-
|
|
158
|
-
def node_subst_cost(uattr, vattr):
|
|
159
|
-
if uattr["color"] == vattr["color"]:
|
|
160
|
-
return 1
|
|
161
|
-
else:
|
|
162
|
-
return 10
|
|
163
|
-
|
|
164
|
-
def node_del_cost(attr):
|
|
165
|
-
if attr["color"] == "blue":
|
|
166
|
-
return 20
|
|
167
|
-
else:
|
|
168
|
-
return 50
|
|
169
|
-
|
|
170
|
-
def node_ins_cost(attr):
|
|
171
|
-
if attr["color"] == "blue":
|
|
172
|
-
return 40
|
|
173
|
-
else:
|
|
174
|
-
return 100
|
|
175
|
-
|
|
176
|
-
assert (
|
|
177
|
-
graph_edit_distance(
|
|
178
|
-
G1,
|
|
179
|
-
G2,
|
|
180
|
-
node_subst_cost=node_subst_cost,
|
|
181
|
-
node_del_cost=node_del_cost,
|
|
182
|
-
node_ins_cost=node_ins_cost,
|
|
183
|
-
)
|
|
184
|
-
== 6
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
def test_graph_edit_distance_edge_cost(self):
|
|
188
|
-
G1 = path_graph(6)
|
|
189
|
-
G2 = path_graph(6)
|
|
190
|
-
for e, attr in G1.edges.items():
|
|
191
|
-
attr["color"] = "red" if min(e) % 2 == 0 else "blue"
|
|
192
|
-
for e, attr in G2.edges.items():
|
|
193
|
-
attr["color"] = "red" if min(e) // 3 == 0 else "blue"
|
|
194
|
-
|
|
195
|
-
def edge_subst_cost(gattr, hattr):
|
|
196
|
-
if gattr["color"] == hattr["color"]:
|
|
197
|
-
return 0.01
|
|
198
|
-
else:
|
|
199
|
-
return 0.1
|
|
200
|
-
|
|
201
|
-
def edge_del_cost(attr):
|
|
202
|
-
if attr["color"] == "blue":
|
|
203
|
-
return 0.2
|
|
204
|
-
else:
|
|
205
|
-
return 0.5
|
|
206
|
-
|
|
207
|
-
def edge_ins_cost(attr):
|
|
208
|
-
if attr["color"] == "blue":
|
|
209
|
-
return 0.4
|
|
210
|
-
else:
|
|
211
|
-
return 1.0
|
|
212
|
-
|
|
213
|
-
assert (
|
|
214
|
-
graph_edit_distance(
|
|
215
|
-
G1,
|
|
216
|
-
G2,
|
|
217
|
-
edge_subst_cost=edge_subst_cost,
|
|
218
|
-
edge_del_cost=edge_del_cost,
|
|
219
|
-
edge_ins_cost=edge_ins_cost,
|
|
220
|
-
)
|
|
221
|
-
== 0.23
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
def test_graph_edit_distance_upper_bound(self):
|
|
225
|
-
G1 = circular_ladder_graph(2)
|
|
226
|
-
G2 = circular_ladder_graph(6)
|
|
227
|
-
assert graph_edit_distance(G1, G2, upper_bound=5) is None
|
|
228
|
-
assert graph_edit_distance(G1, G2, upper_bound=24) == 22
|
|
229
|
-
assert graph_edit_distance(G1, G2) == 22
|
|
230
|
-
|
|
231
|
-
def test_optimal_edit_paths(self):
|
|
232
|
-
G1 = path_graph(3)
|
|
233
|
-
G2 = cycle_graph(3)
|
|
234
|
-
paths, cost = optimal_edit_paths(G1, G2)
|
|
235
|
-
assert cost == 1
|
|
236
|
-
assert len(paths) == 6
|
|
237
|
-
|
|
238
|
-
def canonical(vertex_path, edge_path):
|
|
239
|
-
return (
|
|
240
|
-
tuple(sorted(vertex_path)),
|
|
241
|
-
tuple(sorted(edge_path, key=lambda x: (None in x, x))),
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
expected_paths = [
|
|
245
|
-
(
|
|
246
|
-
[(0, 0), (1, 1), (2, 2)],
|
|
247
|
-
[((0, 1), (0, 1)), ((1, 2), (1, 2)), (None, (0, 2))],
|
|
248
|
-
),
|
|
249
|
-
(
|
|
250
|
-
[(0, 0), (1, 2), (2, 1)],
|
|
251
|
-
[((0, 1), (0, 2)), ((1, 2), (1, 2)), (None, (0, 1))],
|
|
252
|
-
),
|
|
253
|
-
(
|
|
254
|
-
[(0, 1), (1, 0), (2, 2)],
|
|
255
|
-
[((0, 1), (0, 1)), ((1, 2), (0, 2)), (None, (1, 2))],
|
|
256
|
-
),
|
|
257
|
-
(
|
|
258
|
-
[(0, 1), (1, 2), (2, 0)],
|
|
259
|
-
[((0, 1), (1, 2)), ((1, 2), (0, 2)), (None, (0, 1))],
|
|
260
|
-
),
|
|
261
|
-
(
|
|
262
|
-
[(0, 2), (1, 0), (2, 1)],
|
|
263
|
-
[((0, 1), (0, 2)), ((1, 2), (0, 1)), (None, (1, 2))],
|
|
264
|
-
),
|
|
265
|
-
(
|
|
266
|
-
[(0, 2), (1, 1), (2, 0)],
|
|
267
|
-
[((0, 1), (1, 2)), ((1, 2), (0, 1)), (None, (0, 2))],
|
|
268
|
-
),
|
|
269
|
-
]
|
|
270
|
-
assert {canonical(*p) for p in paths} == {canonical(*p) for p in expected_paths}
|
|
271
|
-
|
|
272
|
-
def test_optimize_graph_edit_distance(self):
|
|
273
|
-
G1 = circular_ladder_graph(2)
|
|
274
|
-
G2 = circular_ladder_graph(6)
|
|
275
|
-
bestcost = 1000
|
|
276
|
-
for cost in optimize_graph_edit_distance(G1, G2):
|
|
277
|
-
assert cost < bestcost
|
|
278
|
-
bestcost = cost
|
|
279
|
-
assert bestcost == 22
|
|
280
|
-
|
|
281
|
-
# def test_graph_edit_distance_bigger(self):
|
|
282
|
-
# G1 = circular_ladder_graph(12)
|
|
283
|
-
# G2 = circular_ladder_graph(16)
|
|
284
|
-
# assert_equal(graph_edit_distance(G1, G2), 22)
|
|
285
|
-
|
|
286
|
-
def test_selfloops(self):
|
|
287
|
-
G0 = nx.Graph()
|
|
288
|
-
G1 = nx.Graph()
|
|
289
|
-
G1.add_edges_from((("A", "A"), ("A", "B")))
|
|
290
|
-
G2 = nx.Graph()
|
|
291
|
-
G2.add_edges_from((("A", "B"), ("B", "B")))
|
|
292
|
-
G3 = nx.Graph()
|
|
293
|
-
G3.add_edges_from((("A", "A"), ("A", "B"), ("B", "B")))
|
|
294
|
-
|
|
295
|
-
assert graph_edit_distance(G0, G0) == 0
|
|
296
|
-
assert graph_edit_distance(G0, G1) == 4
|
|
297
|
-
assert graph_edit_distance(G1, G0) == 4
|
|
298
|
-
assert graph_edit_distance(G0, G2) == 4
|
|
299
|
-
assert graph_edit_distance(G2, G0) == 4
|
|
300
|
-
assert graph_edit_distance(G0, G3) == 5
|
|
301
|
-
assert graph_edit_distance(G3, G0) == 5
|
|
302
|
-
|
|
303
|
-
assert graph_edit_distance(G1, G1) == 0
|
|
304
|
-
assert graph_edit_distance(G1, G2) == 0
|
|
305
|
-
assert graph_edit_distance(G2, G1) == 0
|
|
306
|
-
assert graph_edit_distance(G1, G3) == 1
|
|
307
|
-
assert graph_edit_distance(G3, G1) == 1
|
|
308
|
-
|
|
309
|
-
assert graph_edit_distance(G2, G2) == 0
|
|
310
|
-
assert graph_edit_distance(G2, G3) == 1
|
|
311
|
-
assert graph_edit_distance(G3, G2) == 1
|
|
312
|
-
|
|
313
|
-
assert graph_edit_distance(G3, G3) == 0
|
|
314
|
-
|
|
315
|
-
def test_digraph(self):
|
|
316
|
-
G0 = nx.DiGraph()
|
|
317
|
-
G1 = nx.DiGraph()
|
|
318
|
-
G1.add_edges_from((("A", "B"), ("B", "C"), ("C", "D"), ("D", "A")))
|
|
319
|
-
G2 = nx.DiGraph()
|
|
320
|
-
G2.add_edges_from((("A", "B"), ("B", "C"), ("C", "D"), ("A", "D")))
|
|
321
|
-
G3 = nx.DiGraph()
|
|
322
|
-
G3.add_edges_from((("A", "B"), ("A", "C"), ("B", "D"), ("C", "D")))
|
|
323
|
-
|
|
324
|
-
assert graph_edit_distance(G0, G0) == 0
|
|
325
|
-
assert graph_edit_distance(G0, G1) == 8
|
|
326
|
-
assert graph_edit_distance(G1, G0) == 8
|
|
327
|
-
assert graph_edit_distance(G0, G2) == 8
|
|
328
|
-
assert graph_edit_distance(G2, G0) == 8
|
|
329
|
-
assert graph_edit_distance(G0, G3) == 8
|
|
330
|
-
assert graph_edit_distance(G3, G0) == 8
|
|
331
|
-
|
|
332
|
-
assert graph_edit_distance(G1, G1) == 0
|
|
333
|
-
assert graph_edit_distance(G1, G2) == 2
|
|
334
|
-
assert graph_edit_distance(G2, G1) == 2
|
|
335
|
-
assert graph_edit_distance(G1, G3) == 4
|
|
336
|
-
assert graph_edit_distance(G3, G1) == 4
|
|
337
|
-
|
|
338
|
-
assert graph_edit_distance(G2, G2) == 0
|
|
339
|
-
assert graph_edit_distance(G2, G3) == 2
|
|
340
|
-
assert graph_edit_distance(G3, G2) == 2
|
|
341
|
-
|
|
342
|
-
assert graph_edit_distance(G3, G3) == 0
|
|
343
|
-
|
|
344
|
-
def test_multigraph(self):
|
|
345
|
-
G0 = nx.MultiGraph()
|
|
346
|
-
G1 = nx.MultiGraph()
|
|
347
|
-
G1.add_edges_from((("A", "B"), ("B", "C"), ("A", "C")))
|
|
348
|
-
G2 = nx.MultiGraph()
|
|
349
|
-
G2.add_edges_from((("A", "B"), ("B", "C"), ("B", "C"), ("A", "C")))
|
|
350
|
-
G3 = nx.MultiGraph()
|
|
351
|
-
G3.add_edges_from((("A", "B"), ("B", "C"), ("A", "C"), ("A", "C"), ("A", "C")))
|
|
352
|
-
|
|
353
|
-
assert graph_edit_distance(G0, G0) == 0
|
|
354
|
-
assert graph_edit_distance(G0, G1) == 6
|
|
355
|
-
assert graph_edit_distance(G1, G0) == 6
|
|
356
|
-
assert graph_edit_distance(G0, G2) == 7
|
|
357
|
-
assert graph_edit_distance(G2, G0) == 7
|
|
358
|
-
assert graph_edit_distance(G0, G3) == 8
|
|
359
|
-
assert graph_edit_distance(G3, G0) == 8
|
|
360
|
-
|
|
361
|
-
assert graph_edit_distance(G1, G1) == 0
|
|
362
|
-
assert graph_edit_distance(G1, G2) == 1
|
|
363
|
-
assert graph_edit_distance(G2, G1) == 1
|
|
364
|
-
assert graph_edit_distance(G1, G3) == 2
|
|
365
|
-
assert graph_edit_distance(G3, G1) == 2
|
|
366
|
-
|
|
367
|
-
assert graph_edit_distance(G2, G2) == 0
|
|
368
|
-
assert graph_edit_distance(G2, G3) == 1
|
|
369
|
-
assert graph_edit_distance(G3, G2) == 1
|
|
370
|
-
|
|
371
|
-
assert graph_edit_distance(G3, G3) == 0
|
|
372
|
-
|
|
373
|
-
def test_multidigraph(self):
|
|
374
|
-
G1 = nx.MultiDiGraph()
|
|
375
|
-
G1.add_edges_from(
|
|
376
|
-
(
|
|
377
|
-
("hardware", "kernel"),
|
|
378
|
-
("kernel", "hardware"),
|
|
379
|
-
("kernel", "userspace"),
|
|
380
|
-
("userspace", "kernel"),
|
|
381
|
-
)
|
|
382
|
-
)
|
|
383
|
-
G2 = nx.MultiDiGraph()
|
|
384
|
-
G2.add_edges_from(
|
|
385
|
-
(
|
|
386
|
-
("winter", "spring"),
|
|
387
|
-
("spring", "summer"),
|
|
388
|
-
("summer", "autumn"),
|
|
389
|
-
("autumn", "winter"),
|
|
390
|
-
)
|
|
391
|
-
)
|
|
392
|
-
|
|
393
|
-
assert graph_edit_distance(G1, G2) == 5
|
|
394
|
-
assert graph_edit_distance(G2, G1) == 5
|
|
395
|
-
|
|
396
|
-
# by https://github.com/jfbeaumont
|
|
397
|
-
def testCopy(self):
|
|
398
|
-
G = nx.Graph()
|
|
399
|
-
G.add_node("A", label="A")
|
|
400
|
-
G.add_node("B", label="B")
|
|
401
|
-
G.add_edge("A", "B", label="a-b")
|
|
402
|
-
assert (
|
|
403
|
-
graph_edit_distance(G, G.copy(), node_match=nmatch, edge_match=ematch) == 0
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
def testSame(self):
|
|
407
|
-
G1 = nx.Graph()
|
|
408
|
-
G1.add_node("A", label="A")
|
|
409
|
-
G1.add_node("B", label="B")
|
|
410
|
-
G1.add_edge("A", "B", label="a-b")
|
|
411
|
-
G2 = nx.Graph()
|
|
412
|
-
G2.add_node("A", label="A")
|
|
413
|
-
G2.add_node("B", label="B")
|
|
414
|
-
G2.add_edge("A", "B", label="a-b")
|
|
415
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 0
|
|
416
|
-
|
|
417
|
-
def testOneEdgeLabelDiff(self):
|
|
418
|
-
G1 = nx.Graph()
|
|
419
|
-
G1.add_node("A", label="A")
|
|
420
|
-
G1.add_node("B", label="B")
|
|
421
|
-
G1.add_edge("A", "B", label="a-b")
|
|
422
|
-
G2 = nx.Graph()
|
|
423
|
-
G2.add_node("A", label="A")
|
|
424
|
-
G2.add_node("B", label="B")
|
|
425
|
-
G2.add_edge("A", "B", label="bad")
|
|
426
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
|
|
427
|
-
|
|
428
|
-
def testOneNodeLabelDiff(self):
|
|
429
|
-
G1 = nx.Graph()
|
|
430
|
-
G1.add_node("A", label="A")
|
|
431
|
-
G1.add_node("B", label="B")
|
|
432
|
-
G1.add_edge("A", "B", label="a-b")
|
|
433
|
-
G2 = nx.Graph()
|
|
434
|
-
G2.add_node("A", label="Z")
|
|
435
|
-
G2.add_node("B", label="B")
|
|
436
|
-
G2.add_edge("A", "B", label="a-b")
|
|
437
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
|
|
438
|
-
|
|
439
|
-
def testOneExtraNode(self):
|
|
440
|
-
G1 = nx.Graph()
|
|
441
|
-
G1.add_node("A", label="A")
|
|
442
|
-
G1.add_node("B", label="B")
|
|
443
|
-
G1.add_edge("A", "B", label="a-b")
|
|
444
|
-
G2 = nx.Graph()
|
|
445
|
-
G2.add_node("A", label="A")
|
|
446
|
-
G2.add_node("B", label="B")
|
|
447
|
-
G2.add_edge("A", "B", label="a-b")
|
|
448
|
-
G2.add_node("C", label="C")
|
|
449
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
|
|
450
|
-
|
|
451
|
-
def testOneExtraEdge(self):
|
|
452
|
-
G1 = nx.Graph()
|
|
453
|
-
G1.add_node("A", label="A")
|
|
454
|
-
G1.add_node("B", label="B")
|
|
455
|
-
G1.add_node("C", label="C")
|
|
456
|
-
G1.add_node("C", label="C")
|
|
457
|
-
G1.add_edge("A", "B", label="a-b")
|
|
458
|
-
G2 = nx.Graph()
|
|
459
|
-
G2.add_node("A", label="A")
|
|
460
|
-
G2.add_node("B", label="B")
|
|
461
|
-
G2.add_node("C", label="C")
|
|
462
|
-
G2.add_edge("A", "B", label="a-b")
|
|
463
|
-
G2.add_edge("A", "C", label="a-c")
|
|
464
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
|
|
465
|
-
|
|
466
|
-
def testOneExtraNodeAndEdge(self):
|
|
467
|
-
G1 = nx.Graph()
|
|
468
|
-
G1.add_node("A", label="A")
|
|
469
|
-
G1.add_node("B", label="B")
|
|
470
|
-
G1.add_edge("A", "B", label="a-b")
|
|
471
|
-
G2 = nx.Graph()
|
|
472
|
-
G2.add_node("A", label="A")
|
|
473
|
-
G2.add_node("B", label="B")
|
|
474
|
-
G2.add_node("C", label="C")
|
|
475
|
-
G2.add_edge("A", "B", label="a-b")
|
|
476
|
-
G2.add_edge("A", "C", label="a-c")
|
|
477
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 2
|
|
478
|
-
|
|
479
|
-
def testGraph1(self):
|
|
480
|
-
G1 = getCanonical()
|
|
481
|
-
G2 = nx.Graph()
|
|
482
|
-
G2.add_node("A", label="A")
|
|
483
|
-
G2.add_node("B", label="B")
|
|
484
|
-
G2.add_node("D", label="D")
|
|
485
|
-
G2.add_node("E", label="E")
|
|
486
|
-
G2.add_edge("A", "B", label="a-b")
|
|
487
|
-
G2.add_edge("B", "D", label="b-d")
|
|
488
|
-
G2.add_edge("D", "E", label="d-e")
|
|
489
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 3
|
|
490
|
-
|
|
491
|
-
def testGraph2(self):
|
|
492
|
-
G1 = getCanonical()
|
|
493
|
-
G2 = nx.Graph()
|
|
494
|
-
G2.add_node("A", label="A")
|
|
495
|
-
G2.add_node("B", label="B")
|
|
496
|
-
G2.add_node("C", label="C")
|
|
497
|
-
G2.add_node("D", label="D")
|
|
498
|
-
G2.add_node("E", label="E")
|
|
499
|
-
G2.add_edge("A", "B", label="a-b")
|
|
500
|
-
G2.add_edge("B", "C", label="b-c")
|
|
501
|
-
G2.add_edge("C", "D", label="c-d")
|
|
502
|
-
G2.add_edge("C", "E", label="c-e")
|
|
503
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 4
|
|
504
|
-
|
|
505
|
-
def testGraph3(self):
|
|
506
|
-
G1 = getCanonical()
|
|
507
|
-
G2 = nx.Graph()
|
|
508
|
-
G2.add_node("A", label="A")
|
|
509
|
-
G2.add_node("B", label="B")
|
|
510
|
-
G2.add_node("C", label="C")
|
|
511
|
-
G2.add_node("D", label="D")
|
|
512
|
-
G2.add_node("E", label="E")
|
|
513
|
-
G2.add_node("F", label="F")
|
|
514
|
-
G2.add_node("G", label="G")
|
|
515
|
-
G2.add_edge("A", "C", label="a-c")
|
|
516
|
-
G2.add_edge("A", "D", label="a-d")
|
|
517
|
-
G2.add_edge("D", "E", label="d-e")
|
|
518
|
-
G2.add_edge("D", "F", label="d-f")
|
|
519
|
-
G2.add_edge("D", "G", label="d-g")
|
|
520
|
-
G2.add_edge("E", "B", label="e-b")
|
|
521
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 12
|
|
522
|
-
|
|
523
|
-
def testGraph4(self):
|
|
524
|
-
G1 = getCanonical()
|
|
525
|
-
G2 = nx.Graph()
|
|
526
|
-
G2.add_node("A", label="A")
|
|
527
|
-
G2.add_node("B", label="B")
|
|
528
|
-
G2.add_node("C", label="C")
|
|
529
|
-
G2.add_node("D", label="D")
|
|
530
|
-
G2.add_edge("A", "B", label="a-b")
|
|
531
|
-
G2.add_edge("B", "C", label="b-c")
|
|
532
|
-
G2.add_edge("C", "D", label="c-d")
|
|
533
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 2
|
|
534
|
-
|
|
535
|
-
def testGraph4_a(self):
|
|
536
|
-
G1 = getCanonical()
|
|
537
|
-
G2 = nx.Graph()
|
|
538
|
-
G2.add_node("A", label="A")
|
|
539
|
-
G2.add_node("B", label="B")
|
|
540
|
-
G2.add_node("C", label="C")
|
|
541
|
-
G2.add_node("D", label="D")
|
|
542
|
-
G2.add_edge("A", "B", label="a-b")
|
|
543
|
-
G2.add_edge("B", "C", label="b-c")
|
|
544
|
-
G2.add_edge("A", "D", label="a-d")
|
|
545
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 2
|
|
546
|
-
|
|
547
|
-
def testGraph4_b(self):
|
|
548
|
-
G1 = getCanonical()
|
|
549
|
-
G2 = nx.Graph()
|
|
550
|
-
G2.add_node("A", label="A")
|
|
551
|
-
G2.add_node("B", label="B")
|
|
552
|
-
G2.add_node("C", label="C")
|
|
553
|
-
G2.add_node("D", label="D")
|
|
554
|
-
G2.add_edge("A", "B", label="a-b")
|
|
555
|
-
G2.add_edge("B", "C", label="b-c")
|
|
556
|
-
G2.add_edge("B", "D", label="bad")
|
|
557
|
-
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
|
|
558
|
-
|
|
559
|
-
# note: nx.simrank_similarity_numpy not included because returns np.array
|
|
560
|
-
simrank_algs = [
|
|
561
|
-
nx.simrank_similarity,
|
|
562
|
-
nx.algorithms.similarity._simrank_similarity_python,
|
|
563
|
-
]
|
|
564
|
-
|
|
565
|
-
@pytest.mark.parametrize("simrank_similarity", simrank_algs)
|
|
566
|
-
def test_simrank_no_source_no_target(self, simrank_similarity):
|
|
567
|
-
G = nx.cycle_graph(5)
|
|
568
|
-
expected = {
|
|
569
|
-
0: {
|
|
570
|
-
0: 1,
|
|
571
|
-
1: 0.3951219505902448,
|
|
572
|
-
2: 0.5707317069281646,
|
|
573
|
-
3: 0.5707317069281646,
|
|
574
|
-
4: 0.3951219505902449,
|
|
575
|
-
},
|
|
576
|
-
1: {
|
|
577
|
-
0: 0.3951219505902448,
|
|
578
|
-
1: 1,
|
|
579
|
-
2: 0.3951219505902449,
|
|
580
|
-
3: 0.5707317069281646,
|
|
581
|
-
4: 0.5707317069281646,
|
|
582
|
-
},
|
|
583
|
-
2: {
|
|
584
|
-
0: 0.5707317069281646,
|
|
585
|
-
1: 0.3951219505902449,
|
|
586
|
-
2: 1,
|
|
587
|
-
3: 0.3951219505902449,
|
|
588
|
-
4: 0.5707317069281646,
|
|
589
|
-
},
|
|
590
|
-
3: {
|
|
591
|
-
0: 0.5707317069281646,
|
|
592
|
-
1: 0.5707317069281646,
|
|
593
|
-
2: 0.3951219505902449,
|
|
594
|
-
3: 1,
|
|
595
|
-
4: 0.3951219505902449,
|
|
596
|
-
},
|
|
597
|
-
4: {
|
|
598
|
-
0: 0.3951219505902449,
|
|
599
|
-
1: 0.5707317069281646,
|
|
600
|
-
2: 0.5707317069281646,
|
|
601
|
-
3: 0.3951219505902449,
|
|
602
|
-
4: 1,
|
|
603
|
-
},
|
|
604
|
-
}
|
|
605
|
-
actual = simrank_similarity(G)
|
|
606
|
-
for k, v in expected.items():
|
|
607
|
-
assert v == pytest.approx(actual[k], abs=1e-2)
|
|
608
|
-
|
|
609
|
-
# For a DiGraph test, use the first graph from the paper cited in
|
|
610
|
-
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
|
|
611
|
-
G = nx.DiGraph()
|
|
612
|
-
G.add_node(0, label="Univ")
|
|
613
|
-
G.add_node(1, label="ProfA")
|
|
614
|
-
G.add_node(2, label="ProfB")
|
|
615
|
-
G.add_node(3, label="StudentA")
|
|
616
|
-
G.add_node(4, label="StudentB")
|
|
617
|
-
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
|
|
618
|
-
|
|
619
|
-
expected = {
|
|
620
|
-
0: {0: 1, 1: 0.0, 2: 0.1323363991265798, 3: 0.0, 4: 0.03387811817640443},
|
|
621
|
-
1: {0: 0.0, 1: 1, 2: 0.4135512472705618, 3: 0.0, 4: 0.10586911930126384},
|
|
622
|
-
2: {
|
|
623
|
-
0: 0.1323363991265798,
|
|
624
|
-
1: 0.4135512472705618,
|
|
625
|
-
2: 1,
|
|
626
|
-
3: 0.04234764772050554,
|
|
627
|
-
4: 0.08822426608438655,
|
|
628
|
-
},
|
|
629
|
-
3: {0: 0.0, 1: 0.0, 2: 0.04234764772050554, 3: 1, 4: 0.3308409978164495},
|
|
630
|
-
4: {
|
|
631
|
-
0: 0.03387811817640443,
|
|
632
|
-
1: 0.10586911930126384,
|
|
633
|
-
2: 0.08822426608438655,
|
|
634
|
-
3: 0.3308409978164495,
|
|
635
|
-
4: 1,
|
|
636
|
-
},
|
|
637
|
-
}
|
|
638
|
-
# Use the importance_factor from the paper to get the same numbers.
|
|
639
|
-
actual = simrank_similarity(G, importance_factor=0.8)
|
|
640
|
-
for k, v in expected.items():
|
|
641
|
-
assert v == pytest.approx(actual[k], abs=1e-2)
|
|
642
|
-
|
|
643
|
-
@pytest.mark.parametrize("simrank_similarity", simrank_algs)
|
|
644
|
-
def test_simrank_source_no_target(self, simrank_similarity):
|
|
645
|
-
G = nx.cycle_graph(5)
|
|
646
|
-
expected = {
|
|
647
|
-
0: 1,
|
|
648
|
-
1: 0.3951219505902448,
|
|
649
|
-
2: 0.5707317069281646,
|
|
650
|
-
3: 0.5707317069281646,
|
|
651
|
-
4: 0.3951219505902449,
|
|
652
|
-
}
|
|
653
|
-
actual = simrank_similarity(G, source=0)
|
|
654
|
-
assert expected == pytest.approx(actual, abs=1e-2)
|
|
655
|
-
|
|
656
|
-
# For a DiGraph test, use the first graph from the paper cited in
|
|
657
|
-
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
|
|
658
|
-
G = nx.DiGraph()
|
|
659
|
-
G.add_node(0, label="Univ")
|
|
660
|
-
G.add_node(1, label="ProfA")
|
|
661
|
-
G.add_node(2, label="ProfB")
|
|
662
|
-
G.add_node(3, label="StudentA")
|
|
663
|
-
G.add_node(4, label="StudentB")
|
|
664
|
-
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
|
|
665
|
-
|
|
666
|
-
expected = {0: 1, 1: 0.0, 2: 0.1323363991265798, 3: 0.0, 4: 0.03387811817640443}
|
|
667
|
-
# Use the importance_factor from the paper to get the same numbers.
|
|
668
|
-
actual = simrank_similarity(G, importance_factor=0.8, source=0)
|
|
669
|
-
assert expected == pytest.approx(actual, abs=1e-2)
|
|
670
|
-
|
|
671
|
-
@pytest.mark.parametrize("simrank_similarity", simrank_algs)
|
|
672
|
-
def test_simrank_noninteger_nodes(self, simrank_similarity):
|
|
673
|
-
G = nx.cycle_graph(5)
|
|
674
|
-
G = nx.relabel_nodes(G, dict(enumerate("abcde")))
|
|
675
|
-
expected = {
|
|
676
|
-
"a": 1,
|
|
677
|
-
"b": 0.3951219505902448,
|
|
678
|
-
"c": 0.5707317069281646,
|
|
679
|
-
"d": 0.5707317069281646,
|
|
680
|
-
"e": 0.3951219505902449,
|
|
681
|
-
}
|
|
682
|
-
actual = simrank_similarity(G, source="a")
|
|
683
|
-
assert expected == pytest.approx(actual, abs=1e-2)
|
|
684
|
-
|
|
685
|
-
# For a DiGraph test, use the first graph from the paper cited in
|
|
686
|
-
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
|
|
687
|
-
G = nx.DiGraph()
|
|
688
|
-
G.add_node(0, label="Univ")
|
|
689
|
-
G.add_node(1, label="ProfA")
|
|
690
|
-
G.add_node(2, label="ProfB")
|
|
691
|
-
G.add_node(3, label="StudentA")
|
|
692
|
-
G.add_node(4, label="StudentB")
|
|
693
|
-
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
|
|
694
|
-
node_labels = dict(enumerate(nx.get_node_attributes(G, "label").values()))
|
|
695
|
-
G = nx.relabel_nodes(G, node_labels)
|
|
696
|
-
|
|
697
|
-
expected = {
|
|
698
|
-
"Univ": 1,
|
|
699
|
-
"ProfA": 0.0,
|
|
700
|
-
"ProfB": 0.1323363991265798,
|
|
701
|
-
"StudentA": 0.0,
|
|
702
|
-
"StudentB": 0.03387811817640443,
|
|
703
|
-
}
|
|
704
|
-
# Use the importance_factor from the paper to get the same numbers.
|
|
705
|
-
actual = simrank_similarity(G, importance_factor=0.8, source="Univ")
|
|
706
|
-
assert expected == pytest.approx(actual, abs=1e-2)
|
|
707
|
-
|
|
708
|
-
@pytest.mark.parametrize("simrank_similarity", simrank_algs)
|
|
709
|
-
def test_simrank_source_and_target(self, simrank_similarity):
|
|
710
|
-
G = nx.cycle_graph(5)
|
|
711
|
-
expected = 1
|
|
712
|
-
actual = simrank_similarity(G, source=0, target=0)
|
|
713
|
-
assert expected == pytest.approx(actual, abs=1e-2)
|
|
714
|
-
|
|
715
|
-
# For a DiGraph test, use the first graph from the paper cited in
|
|
716
|
-
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
|
|
717
|
-
G = nx.DiGraph()
|
|
718
|
-
G.add_node(0, label="Univ")
|
|
719
|
-
G.add_node(1, label="ProfA")
|
|
720
|
-
G.add_node(2, label="ProfB")
|
|
721
|
-
G.add_node(3, label="StudentA")
|
|
722
|
-
G.add_node(4, label="StudentB")
|
|
723
|
-
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
|
|
724
|
-
|
|
725
|
-
expected = 0.1323363991265798
|
|
726
|
-
# Use the importance_factor from the paper to get the same numbers.
|
|
727
|
-
# Use the pair (0,2) because (0,0) and (0,1) have trivial results.
|
|
728
|
-
actual = simrank_similarity(G, importance_factor=0.8, source=0, target=2)
|
|
729
|
-
assert expected == pytest.approx(actual, abs=1e-5)
|
|
730
|
-
|
|
731
|
-
@pytest.mark.parametrize("alg", simrank_algs)
|
|
732
|
-
def test_simrank_max_iterations(self, alg):
|
|
733
|
-
G = nx.cycle_graph(5)
|
|
734
|
-
pytest.raises(nx.ExceededMaxIterations, alg, G, max_iterations=10)
|
|
735
|
-
|
|
736
|
-
def test_simrank_source_not_found(self):
|
|
737
|
-
G = nx.cycle_graph(5)
|
|
738
|
-
with pytest.raises(nx.NodeNotFound, match="Source node 10 not in G"):
|
|
739
|
-
nx.simrank_similarity(G, source=10)
|
|
740
|
-
|
|
741
|
-
def test_simrank_target_not_found(self):
|
|
742
|
-
G = nx.cycle_graph(5)
|
|
743
|
-
with pytest.raises(nx.NodeNotFound, match="Target node 10 not in G"):
|
|
744
|
-
nx.simrank_similarity(G, target=10)
|
|
745
|
-
|
|
746
|
-
def test_simrank_between_versions(self):
|
|
747
|
-
G = nx.cycle_graph(5)
|
|
748
|
-
# _python tolerance 1e-4
|
|
749
|
-
expected_python_tol4 = {
|
|
750
|
-
0: 1,
|
|
751
|
-
1: 0.394512499239852,
|
|
752
|
-
2: 0.5703550452791322,
|
|
753
|
-
3: 0.5703550452791323,
|
|
754
|
-
4: 0.394512499239852,
|
|
755
|
-
}
|
|
756
|
-
# _numpy tolerance 1e-4
|
|
757
|
-
expected_numpy_tol4 = {
|
|
758
|
-
0: 1.0,
|
|
759
|
-
1: 0.3947180735764555,
|
|
760
|
-
2: 0.570482097206368,
|
|
761
|
-
3: 0.570482097206368,
|
|
762
|
-
4: 0.3947180735764555,
|
|
763
|
-
}
|
|
764
|
-
actual = nx.simrank_similarity(G, source=0)
|
|
765
|
-
assert expected_numpy_tol4 == pytest.approx(actual, abs=1e-7)
|
|
766
|
-
# versions differ at 1e-4 level but equal at 1e-3
|
|
767
|
-
assert expected_python_tol4 != pytest.approx(actual, abs=1e-4)
|
|
768
|
-
assert expected_python_tol4 == pytest.approx(actual, abs=1e-3)
|
|
769
|
-
|
|
770
|
-
actual = nx.similarity._simrank_similarity_python(G, source=0)
|
|
771
|
-
assert expected_python_tol4 == pytest.approx(actual, abs=1e-7)
|
|
772
|
-
# versions differ at 1e-4 level but equal at 1e-3
|
|
773
|
-
assert expected_numpy_tol4 != pytest.approx(actual, abs=1e-4)
|
|
774
|
-
assert expected_numpy_tol4 == pytest.approx(actual, abs=1e-3)
|
|
775
|
-
|
|
776
|
-
def test_simrank_numpy_no_source_no_target(self):
|
|
777
|
-
G = nx.cycle_graph(5)
|
|
778
|
-
expected = np.array(
|
|
779
|
-
[
|
|
780
|
-
[
|
|
781
|
-
1.0,
|
|
782
|
-
0.3947180735764555,
|
|
783
|
-
0.570482097206368,
|
|
784
|
-
0.570482097206368,
|
|
785
|
-
0.3947180735764555,
|
|
786
|
-
],
|
|
787
|
-
[
|
|
788
|
-
0.3947180735764555,
|
|
789
|
-
1.0,
|
|
790
|
-
0.3947180735764555,
|
|
791
|
-
0.570482097206368,
|
|
792
|
-
0.570482097206368,
|
|
793
|
-
],
|
|
794
|
-
[
|
|
795
|
-
0.570482097206368,
|
|
796
|
-
0.3947180735764555,
|
|
797
|
-
1.0,
|
|
798
|
-
0.3947180735764555,
|
|
799
|
-
0.570482097206368,
|
|
800
|
-
],
|
|
801
|
-
[
|
|
802
|
-
0.570482097206368,
|
|
803
|
-
0.570482097206368,
|
|
804
|
-
0.3947180735764555,
|
|
805
|
-
1.0,
|
|
806
|
-
0.3947180735764555,
|
|
807
|
-
],
|
|
808
|
-
[
|
|
809
|
-
0.3947180735764555,
|
|
810
|
-
0.570482097206368,
|
|
811
|
-
0.570482097206368,
|
|
812
|
-
0.3947180735764555,
|
|
813
|
-
1.0,
|
|
814
|
-
],
|
|
815
|
-
]
|
|
816
|
-
)
|
|
817
|
-
actual = nx.similarity._simrank_similarity_numpy(G)
|
|
818
|
-
np.testing.assert_allclose(expected, actual, atol=1e-7)
|
|
819
|
-
|
|
820
|
-
def test_simrank_numpy_source_no_target(self):
|
|
821
|
-
G = nx.cycle_graph(5)
|
|
822
|
-
expected = np.array(
|
|
823
|
-
[
|
|
824
|
-
1.0,
|
|
825
|
-
0.3947180735764555,
|
|
826
|
-
0.570482097206368,
|
|
827
|
-
0.570482097206368,
|
|
828
|
-
0.3947180735764555,
|
|
829
|
-
]
|
|
830
|
-
)
|
|
831
|
-
actual = nx.similarity._simrank_similarity_numpy(G, source=0)
|
|
832
|
-
np.testing.assert_allclose(expected, actual, atol=1e-7)
|
|
833
|
-
|
|
834
|
-
def test_simrank_numpy_source_and_target(self):
|
|
835
|
-
G = nx.cycle_graph(5)
|
|
836
|
-
expected = 1.0
|
|
837
|
-
actual = nx.similarity._simrank_similarity_numpy(G, source=0, target=0)
|
|
838
|
-
np.testing.assert_allclose(expected, actual, atol=1e-7)
|
|
839
|
-
|
|
840
|
-
def test_panther_similarity_unweighted(self):
|
|
841
|
-
G = nx.Graph()
|
|
842
|
-
G.add_edge(0, 1)
|
|
843
|
-
G.add_edge(0, 2)
|
|
844
|
-
G.add_edge(0, 3)
|
|
845
|
-
G.add_edge(1, 2)
|
|
846
|
-
G.add_edge(2, 4)
|
|
847
|
-
expected = {3: 0.5, 2: 0.5, 1: 0.5, 4: 0.125}
|
|
848
|
-
sim = nx.panther_similarity(G, 0, path_length=2, seed=42)
|
|
849
|
-
assert sim == expected
|
|
850
|
-
|
|
851
|
-
def test_panther_similarity_weighted(self):
|
|
852
|
-
G = nx.Graph()
|
|
853
|
-
G.add_edge("v1", "v2", w=5)
|
|
854
|
-
G.add_edge("v1", "v3", w=1)
|
|
855
|
-
G.add_edge("v1", "v4", w=2)
|
|
856
|
-
G.add_edge("v2", "v3", w=0.1)
|
|
857
|
-
G.add_edge("v3", "v5", w=1)
|
|
858
|
-
expected = {"v3": 0.75, "v4": 0.5, "v2": 0.5, "v5": 0.25}
|
|
859
|
-
sim = nx.panther_similarity(G, "v1", path_length=2, weight="w", seed=42)
|
|
860
|
-
assert sim == expected
|
|
861
|
-
|
|
862
|
-
def test_panther_similarity_source_not_found(self):
|
|
863
|
-
G = nx.Graph()
|
|
864
|
-
G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 2), (2, 4)])
|
|
865
|
-
with pytest.raises(nx.NodeNotFound, match="Source node 10 not in G"):
|
|
866
|
-
nx.panther_similarity(G, source=10)
|
|
867
|
-
|
|
868
|
-
def test_panther_similarity_isolated(self):
|
|
869
|
-
G = nx.Graph()
|
|
870
|
-
G.add_nodes_from(range(5))
|
|
871
|
-
with pytest.raises(
|
|
872
|
-
nx.NetworkXUnfeasible,
|
|
873
|
-
match="Panther similarity is not defined for the isolated source node 1.",
|
|
874
|
-
):
|
|
875
|
-
nx.panther_similarity(G, source=1)
|
|
876
|
-
|
|
877
|
-
@pytest.mark.parametrize("num_paths", (1, 3, 10))
|
|
878
|
-
@pytest.mark.parametrize("source", (0, 1))
|
|
879
|
-
def test_generate_random_paths_with_start(self, num_paths, source):
|
|
880
|
-
G = nx.Graph([(0, 1), (0, 2), (0, 3), (1, 2), (2, 4)])
|
|
881
|
-
index_map = {}
|
|
882
|
-
|
|
883
|
-
path_gen = nx.generate_random_paths(
|
|
884
|
-
G,
|
|
885
|
-
num_paths,
|
|
886
|
-
path_length=2,
|
|
887
|
-
index_map=index_map,
|
|
888
|
-
source=source,
|
|
889
|
-
)
|
|
890
|
-
paths = list(path_gen)
|
|
891
|
-
|
|
892
|
-
# There should be num_paths paths
|
|
893
|
-
assert len(paths) == num_paths
|
|
894
|
-
# And they should all start with `source`
|
|
895
|
-
assert all(p[0] == source for p in paths)
|
|
896
|
-
# The index_map for the `source` node should contain the indices for
|
|
897
|
-
# all of the generated paths.
|
|
898
|
-
assert sorted(index_map[source]) == list(range(num_paths))
|
|
899
|
-
|
|
900
|
-
def test_generate_random_paths_unweighted(self):
|
|
901
|
-
index_map = {}
|
|
902
|
-
num_paths = 10
|
|
903
|
-
path_length = 2
|
|
904
|
-
G = nx.Graph()
|
|
905
|
-
G.add_edge(0, 1)
|
|
906
|
-
G.add_edge(0, 2)
|
|
907
|
-
G.add_edge(0, 3)
|
|
908
|
-
G.add_edge(1, 2)
|
|
909
|
-
G.add_edge(2, 4)
|
|
910
|
-
paths = nx.generate_random_paths(
|
|
911
|
-
G, num_paths, path_length=path_length, index_map=index_map, seed=42
|
|
912
|
-
)
|
|
913
|
-
expected_paths = [
|
|
914
|
-
[3, 0, 3],
|
|
915
|
-
[4, 2, 1],
|
|
916
|
-
[2, 1, 0],
|
|
917
|
-
[2, 0, 3],
|
|
918
|
-
[3, 0, 1],
|
|
919
|
-
[3, 0, 1],
|
|
920
|
-
[4, 2, 0],
|
|
921
|
-
[2, 1, 0],
|
|
922
|
-
[3, 0, 2],
|
|
923
|
-
[2, 1, 2],
|
|
924
|
-
]
|
|
925
|
-
expected_map = {
|
|
926
|
-
0: {0, 2, 3, 4, 5, 6, 7, 8},
|
|
927
|
-
1: {1, 2, 4, 5, 7, 9},
|
|
928
|
-
2: {1, 2, 3, 6, 7, 8, 9},
|
|
929
|
-
3: {0, 3, 4, 5, 8},
|
|
930
|
-
4: {1, 6},
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
assert expected_paths == list(paths)
|
|
934
|
-
assert expected_map == index_map
|
|
935
|
-
|
|
936
|
-
def test_generate_random_paths_weighted(self):
|
|
937
|
-
index_map = {}
|
|
938
|
-
num_paths = 10
|
|
939
|
-
path_length = 6
|
|
940
|
-
G = nx.Graph()
|
|
941
|
-
G.add_edge("a", "b", weight=0.6)
|
|
942
|
-
G.add_edge("a", "c", weight=0.2)
|
|
943
|
-
G.add_edge("c", "d", weight=0.1)
|
|
944
|
-
G.add_edge("c", "e", weight=0.7)
|
|
945
|
-
G.add_edge("c", "f", weight=0.9)
|
|
946
|
-
G.add_edge("a", "d", weight=0.3)
|
|
947
|
-
paths = nx.generate_random_paths(
|
|
948
|
-
G, num_paths, path_length=path_length, index_map=index_map, seed=42
|
|
949
|
-
)
|
|
950
|
-
|
|
951
|
-
expected_paths = [
|
|
952
|
-
["d", "c", "f", "c", "d", "a", "b"],
|
|
953
|
-
["e", "c", "f", "c", "f", "c", "e"],
|
|
954
|
-
["d", "a", "b", "a", "b", "a", "c"],
|
|
955
|
-
["b", "a", "d", "a", "b", "a", "b"],
|
|
956
|
-
["d", "a", "b", "a", "b", "a", "d"],
|
|
957
|
-
["d", "a", "b", "a", "b", "a", "c"],
|
|
958
|
-
["d", "a", "b", "a", "b", "a", "b"],
|
|
959
|
-
["f", "c", "f", "c", "f", "c", "e"],
|
|
960
|
-
["d", "a", "d", "a", "b", "a", "b"],
|
|
961
|
-
["e", "c", "f", "c", "e", "c", "d"],
|
|
962
|
-
]
|
|
963
|
-
expected_map = {
|
|
964
|
-
"d": {0, 2, 3, 4, 5, 6, 8, 9},
|
|
965
|
-
"c": {0, 1, 2, 5, 7, 9},
|
|
966
|
-
"f": {0, 1, 9, 7},
|
|
967
|
-
"a": {0, 2, 3, 4, 5, 6, 8},
|
|
968
|
-
"b": {0, 2, 3, 4, 5, 6, 8},
|
|
969
|
-
"e": {1, 9, 7},
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
assert expected_paths == list(paths)
|
|
973
|
-
assert expected_map == index_map
|
|
974
|
-
|
|
975
|
-
def test_one_node_one_loop_and_empty_graph(self):
|
|
976
|
-
G1 = nx.DiGraph([(0, 0)])
|
|
977
|
-
G2 = nx.DiGraph()
|
|
978
|
-
assert nx.graph_edit_distance(G1, G2) == 2
|
|
979
|
-
|
|
980
|
-
def test_one_node_two_loops_and_empty_graph(self):
|
|
981
|
-
G1 = nx.MultiDiGraph([(0, 0), (0, 0)])
|
|
982
|
-
assert nx.graph_edit_distance(G1, nx.DiGraph()) == 3
|
|
983
|
-
assert nx.graph_edit_distance(G1, nx.MultiDiGraph()) == 3
|
|
984
|
-
|
|
985
|
-
def test_two_directed_loops(self):
|
|
986
|
-
G = nx.DiGraph([(0, 0), (1, 1)])
|
|
987
|
-
assert nx.graph_edit_distance(G, nx.DiGraph()) == 4
|
|
988
|
-
|
|
989
|
-
def test_symmetry_with_custom_matching(self):
|
|
990
|
-
"""G2 has edge (a,b) and G3 has edge (a,a) but node order for G2 is (a,b)
|
|
991
|
-
while for G3 it is (b,a)"""
|
|
992
|
-
|
|
993
|
-
a, b = "A", "B"
|
|
994
|
-
G2 = nx.Graph()
|
|
995
|
-
G2.add_nodes_from((a, b))
|
|
996
|
-
G2.add_edges_from([(a, b)])
|
|
997
|
-
G3 = nx.Graph()
|
|
998
|
-
G3.add_nodes_from((b, a))
|
|
999
|
-
G3.add_edges_from([(a, a)])
|
|
1000
|
-
for G in (G2, G3):
|
|
1001
|
-
for n in G:
|
|
1002
|
-
G.nodes[n]["attr"] = n
|
|
1003
|
-
for e in G.edges:
|
|
1004
|
-
G.edges[e]["attr"] = e
|
|
1005
|
-
|
|
1006
|
-
def user_match(x, y):
|
|
1007
|
-
return x == y
|
|
1008
|
-
|
|
1009
|
-
assert (
|
|
1010
|
-
nx.graph_edit_distance(G2, G3, node_match=user_match, edge_match=user_match)
|
|
1011
|
-
== 1
|
|
1012
|
-
)
|
|
1013
|
-
assert (
|
|
1014
|
-
nx.graph_edit_distance(G3, G2, node_match=user_match, edge_match=user_match)
|
|
1015
|
-
== 1
|
|
1016
|
-
)
|
|
1017
|
-
|
|
1018
|
-
def test_panther_vector_similarity_basic(self):
|
|
1019
|
-
"""Basic test for panther_vector_similarity function."""
|
|
1020
|
-
G = nx.Graph()
|
|
1021
|
-
G.add_edge(0, 1)
|
|
1022
|
-
G.add_edge(0, 2)
|
|
1023
|
-
G.add_edge(0, 3)
|
|
1024
|
-
G.add_edge(1, 2)
|
|
1025
|
-
G.add_edge(2, 4)
|
|
1026
|
-
|
|
1027
|
-
sim = nx.panther_vector_similarity(G, 0, D=3, k=4, path_length=2, seed=42)
|
|
1028
|
-
|
|
1029
|
-
assert len(sim) > 0
|
|
1030
|
-
assert 0 not in sim # Source node should not be included
|
|
1031
|
-
assert all(node in [1, 2, 3, 4] for node in sim) # Only valid nodes
|
|
1032
|
-
assert all(0 <= score <= 1 for score in sim.values()) # Valid scores
|
|
1033
|
-
|
|
1034
|
-
def test_panther_vector_similarity_unweighted(self):
|
|
1035
|
-
"""Test panther_vector_similarity with unweighted graph."""
|
|
1036
|
-
G = nx.Graph()
|
|
1037
|
-
G.add_edge(0, 1)
|
|
1038
|
-
G.add_edge(0, 2)
|
|
1039
|
-
G.add_edge(0, 3)
|
|
1040
|
-
G.add_edge(1, 2)
|
|
1041
|
-
G.add_edge(2, 4)
|
|
1042
|
-
|
|
1043
|
-
sim = nx.panther_vector_similarity(G, 0, D=3, k=4, path_length=2, seed=42)
|
|
1044
|
-
|
|
1045
|
-
assert len(sim) == 4
|
|
1046
|
-
assert 0 not in sim
|
|
1047
|
-
assert all(node in sim for node in [1, 2, 3, 4])
|
|
1048
|
-
assert all(0 <= score <= 1 for score in sim.values())
|
|
1049
|
-
|
|
1050
|
-
def test_panther_vector_similarity_weighted(self):
|
|
1051
|
-
"""Test panther_vector_similarity with weighted graph."""
|
|
1052
|
-
G = nx.Graph()
|
|
1053
|
-
G.add_edge("v1", "v2", weight=5)
|
|
1054
|
-
G.add_edge("v1", "v3", weight=1)
|
|
1055
|
-
G.add_edge("v1", "v4", weight=2)
|
|
1056
|
-
G.add_edge("v2", "v3", weight=0.1)
|
|
1057
|
-
G.add_edge("v3", "v5", weight=1)
|
|
1058
|
-
|
|
1059
|
-
sim = nx.panther_vector_similarity(
|
|
1060
|
-
G, "v1", D=3, k=4, path_length=2, weight="weight", seed=42
|
|
1061
|
-
)
|
|
1062
|
-
|
|
1063
|
-
assert len(sim) == 4
|
|
1064
|
-
assert "v1" not in sim
|
|
1065
|
-
assert all(0 <= score <= 1 for score in sim.values())
|
|
1066
|
-
assert all(node in sim for node in ["v2", "v3", "v4"])
|
|
1067
|
-
|
|
1068
|
-
def test_panther_vector_similarity_source_not_found(self):
|
|
1069
|
-
"""Test panther_vector_similarity with non-existent source node."""
|
|
1070
|
-
G = nx.Graph()
|
|
1071
|
-
G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 2), (2, 4)])
|
|
1072
|
-
|
|
1073
|
-
with pytest.raises(nx.NodeNotFound):
|
|
1074
|
-
nx.panther_vector_similarity(G, source=10)
|
|
1075
|
-
|
|
1076
|
-
def test_panther_vector_similarity_isolated(self):
|
|
1077
|
-
"""Test panther_vector_similarity with isolated source node."""
|
|
1078
|
-
G = nx.Graph()
|
|
1079
|
-
G.add_nodes_from(range(5))
|
|
1080
|
-
G.add_edge(0, 1)
|
|
1081
|
-
|
|
1082
|
-
with pytest.raises(nx.NetworkXUnfeasible):
|
|
1083
|
-
nx.panther_vector_similarity(G, source=2)
|
|
1084
|
-
|
|
1085
|
-
def test_panther_vector_similarity_too_large_D(self):
|
|
1086
|
-
"""Test raises when D > number of nodes."""
|
|
1087
|
-
G = nx.star_graph(3)
|
|
1088
|
-
|
|
1089
|
-
with pytest.raises(nx.NetworkXUnfeasible):
|
|
1090
|
-
nx.panther_vector_similarity(G, 0, D=5, k=3)
|
|
1091
|
-
|
|
1092
|
-
def test_panther_vector_similarity_too_large_k(self):
|
|
1093
|
-
"""Test raises when k > number of nodes."""
|
|
1094
|
-
G = nx.star_graph(3)
|
|
1095
|
-
|
|
1096
|
-
with pytest.raises(nx.NetworkXUnfeasible):
|
|
1097
|
-
nx.panther_vector_similarity(G, 0, k=5)
|
|
1098
|
-
|
|
1099
|
-
def test_panther_vector_similarity_small_graph(self):
|
|
1100
|
-
"""Test panther_vector_similarity with a very small graph."""
|
|
1101
|
-
G = nx.Graph()
|
|
1102
|
-
G.add_edge(0, 1)
|
|
1103
|
-
|
|
1104
|
-
sim = nx.panther_vector_similarity(G, 0, D=2, k=2, seed=42)
|
|
1105
|
-
|
|
1106
|
-
assert len(sim) == 1
|
|
1107
|
-
assert 1 in sim
|
|
1108
|
-
assert sim[1] > 0
|
|
1109
|
-
|
|
1110
|
-
def test_panther_vector_similarity_deterministic(self):
|
|
1111
|
-
"""Test that results are deterministic with fixed seed."""
|
|
1112
|
-
G = nx.Graph()
|
|
1113
|
-
G.add_edges_from([(0, 1), (0, 2), (0, 3), (1, 2), (2, 4)])
|
|
1114
|
-
|
|
1115
|
-
sim1 = nx.panther_vector_similarity(G, 0, D=3, path_length=2, seed=42)
|
|
1116
|
-
|
|
1117
|
-
sim2 = nx.panther_vector_similarity(G, 0, D=3, path_length=2, seed=42)
|
|
1118
|
-
|
|
1119
|
-
assert sim1 == sim2
|
|
1120
|
-
|
|
1121
|
-
def test_panther_similarity_string_nodes(self):
|
|
1122
|
-
"""Test panther_similarity with string node names."""
|
|
1123
|
-
pytest.importorskip("numpy")
|
|
1124
|
-
G = nx.Graph()
|
|
1125
|
-
G.add_edges_from([("A", "B"), ("A", "C"), ("A", "D"), ("B", "C")])
|
|
1126
|
-
|
|
1127
|
-
sim = nx.panther_similarity(G, "A", k=2, path_length=2, seed=42)
|
|
1128
|
-
|
|
1129
|
-
assert "A" not in sim # Source node should not be included
|
|
1130
|
-
assert all(isinstance(node, str) for node in sim) # Nodes should remain strings
|
|
1131
|
-
|
|
1132
|
-
def test_panther_vector_similarity_string_nodes(self):
|
|
1133
|
-
"""Test panther_vector_similarity with string node names."""
|
|
1134
|
-
pytest.importorskip("numpy")
|
|
1135
|
-
G = nx.Graph()
|
|
1136
|
-
G.add_edges_from([("A", "B"), ("A", "C"), ("A", "D"), ("B", "C")])
|
|
1137
|
-
|
|
1138
|
-
sim = nx.panther_vector_similarity(G, "A", D=3, k=2, path_length=2, seed=42)
|
|
1139
|
-
|
|
1140
|
-
assert "A" not in sim # Source node should not be included
|
|
1141
|
-
assert all(isinstance(node, str) for node in sim) # Nodes should remain strings
|
|
1142
|
-
|
|
1143
|
-
def test_panther_similarity_k_parameter_returns_k_results(self):
|
|
1144
|
-
pytest.importorskip("numpy")
|
|
1145
|
-
G = nx.star_graph(100)
|
|
1146
|
-
|
|
1147
|
-
for k_val in [1, 2, 3, 4, 5, 10]:
|
|
1148
|
-
result_panther = nx.panther_similarity(G, source=1, k=k_val, seed=42)
|
|
1149
|
-
assert len(result_panther) == k_val, (
|
|
1150
|
-
f"panther_similarity k={k_val} returned {len(result_panther)} results"
|
|
1151
|
-
)
|
|
1152
|
-
assert 1 not in result_panther, "Source node should not be in results"
|
|
1153
|
-
|
|
1154
|
-
result_vector = nx.panther_vector_similarity(G, source=1, k=k_val, seed=42)
|
|
1155
|
-
assert len(result_vector) == k_val, (
|
|
1156
|
-
f"panther_vector_similarity k={k_val} returned {len(result_vector)} results"
|
|
1157
|
-
)
|
|
1158
|
-
assert 1 not in result_vector, "Source node should not be in results"
|