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.
Files changed (337) hide show
  1. package/docs/kr/architecture.md +2 -2
  2. package/docs/kr/follow-ups/2026-07-10-final-report-option-3.md +51 -0
  3. package/package.json +1 -1
  4. package/runtime/BUILD.json +2 -2
  5. package/runtime/agents/workers/report-writer-worker.md +1 -0
  6. package/runtime/prompts/launch.template.md +34 -0
  7. package/runtime/prompts/lead/convergence.md +2 -0
  8. package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
  9. package/runtime/prompts/lead/report-writer.md +1 -1
  10. package/runtime/prompts/profiles/implementation-planning.md +1 -0
  11. package/runtime/python/okstra_ctl/implementation_stage.py +46 -39
  12. package/runtime/python/okstra_ctl/incremental_carry.py +70 -0
  13. package/runtime/python/okstra_ctl/incremental_scope.py +89 -0
  14. package/runtime/python/okstra_ctl/report_views.py +151 -12
  15. package/runtime/python/okstra_ctl/run.py +24 -24
  16. package/runtime/python/okstra_ctl/stage_targets.py +26 -0
  17. package/runtime/python/okstra_ctl/worktree.py +1 -1
  18. package/runtime/python/okstra_token_usage/claude.py +58 -19
  19. package/runtime/python/okstra_token_usage/collect.py +3 -1
  20. package/runtime/python/okstra_vendor/__init__.py +3 -0
  21. package/runtime/schemas/final-report-v1.0.schema.json +39 -0
  22. package/runtime/templates/reports/final-report.template.md +27 -0
  23. package/runtime/templates/reports/i18n/en.json +23 -12
  24. package/runtime/templates/reports/i18n/ko.json +23 -12
  25. package/runtime/templates/reports/report.css +55 -0
  26. package/runtime/templates/reports/report.js +53 -1
  27. package/runtime/validators/validate-run.py +50 -0
  28. package/src/cli-registry.mjs +14 -0
  29. package/src/commands/execute/incremental-carry.mjs +20 -0
  30. package/src/commands/execute/incremental-scope.mjs +20 -0
  31. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/__init__.py +0 -0
  32. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +0 -41
  33. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_clique.py +0 -112
  34. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_connectivity.py +0 -199
  35. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_density.py +0 -146
  36. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_distance_measures.py +0 -59
  37. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_dominating_set.py +0 -78
  38. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_kcomponents.py +0 -303
  39. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_matching.py +0 -8
  40. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_maxcut.py +0 -94
  41. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_ramsey.py +0 -31
  42. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_steinertree.py +0 -306
  43. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_traveling_salesman.py +0 -1014
  44. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_treewidth.py +0 -274
  45. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_vertex_cover.py +0 -68
  46. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/__init__.py +0 -0
  47. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/base_test.py +0 -81
  48. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_connectivity.py +0 -143
  49. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_correlation.py +0 -122
  50. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_mixing.py +0 -174
  51. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_neighbor_degree.py +0 -107
  52. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_pairs.py +0 -87
  53. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/__init__.py +0 -0
  54. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_basic.py +0 -125
  55. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_centrality.py +0 -192
  56. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_cluster.py +0 -84
  57. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_covering.py +0 -33
  58. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_edgelist.py +0 -240
  59. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_extendability.py +0 -334
  60. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_generators.py +0 -407
  61. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_link_analysis.py +0 -218
  62. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matching.py +0 -327
  63. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matrix.py +0 -138
  64. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_project.py +0 -409
  65. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_redundancy.py +0 -35
  66. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py +0 -80
  67. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/__init__.py +0 -0
  68. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality.py +0 -923
  69. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py +0 -354
  70. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_closeness_centrality.py +0 -274
  71. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py +0 -259
  72. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py +0 -147
  73. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_closeness.py +0 -43
  74. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_degree_centrality.py +0 -144
  75. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_dispersion.py +0 -73
  76. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py +0 -186
  77. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_group.py +0 -277
  78. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_harmonic_centrality.py +0 -122
  79. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_katz_centrality.py +0 -345
  80. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_laplacian_centrality.py +0 -220
  81. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_load_centrality.py +0 -344
  82. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_percolation_centrality.py +0 -87
  83. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_reaching.py +0 -140
  84. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_second_order_centrality.py +0 -82
  85. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_subgraph.py +0 -110
  86. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_trophic.py +0 -302
  87. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_voterank.py +0 -64
  88. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/__init__.py +0 -0
  89. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/test_coloring.py +0 -863
  90. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/__init__.py +0 -0
  91. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_asyn_fluid.py +0 -147
  92. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_bipartitions.py +0 -157
  93. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_centrality.py +0 -85
  94. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_divisive.py +0 -106
  95. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_kclique.py +0 -91
  96. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_label_propagation.py +0 -241
  97. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_leiden.py +0 -138
  98. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_local.py +0 -76
  99. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_louvain.py +0 -264
  100. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_lukes.py +0 -152
  101. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_modularity_max.py +0 -340
  102. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_quality.py +0 -139
  103. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_utils.py +0 -26
  104. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/__init__.py +0 -0
  105. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_attracting.py +0 -70
  106. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_biconnected.py +0 -248
  107. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_connected.py +0 -138
  108. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_semiconnected.py +0 -55
  109. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_strongly_connected.py +0 -193
  110. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_weakly_connected.py +0 -96
  111. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/__init__.py +0 -0
  112. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_connectivity.py +0 -421
  113. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_cuts.py +0 -309
  114. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_disjoint_paths.py +0 -249
  115. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +0 -502
  116. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +0 -488
  117. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcomponents.py +0 -323
  118. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcutsets.py +0 -280
  119. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_stoer_wagner.py +0 -102
  120. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/__init__.py +0 -0
  121. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gl1.gpickle.bz2 +0 -0
  122. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gw1.gpickle.bz2 +0 -0
  123. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/netgen-2.gpickle.bz2 +0 -0
  124. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_gomory_hu.py +0 -128
  125. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow.py +0 -573
  126. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +0 -155
  127. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_mincost.py +0 -475
  128. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_networksimplex.py +0 -481
  129. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/wlm3.gpickle.bz2 +0 -0
  130. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/__init__.py +0 -0
  131. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.A99 +0 -0
  132. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.B99 +0 -0
  133. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.A99 +0 -0
  134. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.B99 +0 -0
  135. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_ismags.py +0 -719
  136. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphism.py +0 -103
  137. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphvf2.py +0 -490
  138. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_match_helpers.py +0 -64
  139. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py +0 -212
  140. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py +0 -202
  141. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp.py +0 -1655
  142. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py +0 -3118
  143. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2userfunc.py +0 -196
  144. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/__init__.py +0 -0
  145. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_hits.py +0 -77
  146. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_pagerank.py +0 -213
  147. package/runtime/python/okstra_vendor/networkx/algorithms/minors/tests/test_contraction.py +0 -544
  148. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/__init__.py +0 -0
  149. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_all.py +0 -328
  150. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_binary.py +0 -451
  151. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_product.py +0 -491
  152. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_unary.py +0 -55
  153. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/__init__.py +0 -0
  154. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_astar.py +0 -254
  155. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense.py +0 -212
  156. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +0 -88
  157. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_generic.py +0 -511
  158. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_unweighted.py +0 -149
  159. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_weighted.py +0 -983
  160. package/runtime/python/okstra_vendor/networkx/algorithms/tests/__init__.py +0 -0
  161. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_asteroidal.py +0 -23
  162. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_boundary.py +0 -154
  163. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_bridges.py +0 -144
  164. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_broadcasting.py +0 -109
  165. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chains.py +0 -136
  166. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chordal.py +0 -129
  167. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_clique.py +0 -300
  168. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cluster.py +0 -678
  169. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_communicability.py +0 -80
  170. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_core.py +0 -266
  171. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_covering.py +0 -85
  172. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cuts.py +0 -171
  173. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cycles.py +0 -984
  174. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_d_separation.py +0 -340
  175. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dag.py +0 -835
  176. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_measures.py +0 -831
  177. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_regular.py +0 -85
  178. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominance.py +0 -299
  179. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominating.py +0 -115
  180. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_efficiency.py +0 -58
  181. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_euler.py +0 -314
  182. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graph_hashing.py +0 -872
  183. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graphical.py +0 -163
  184. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hierarchy.py +0 -46
  185. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hybrid.py +0 -24
  186. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_isolate.py +0 -26
  187. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_link_prediction.py +0 -615
  188. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_lowest_common_ancestors.py +0 -459
  189. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_matching.py +0 -556
  190. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_max_weight_clique.py +0 -179
  191. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_mis.py +0 -62
  192. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_moral.py +0 -15
  193. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_node_classification.py +0 -140
  194. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_non_randomness.py +0 -60
  195. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_perfect_graph.py +0 -27
  196. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planar_drawing.py +0 -274
  197. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planarity.py +0 -556
  198. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_polynomials.py +0 -57
  199. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_reciprocity.py +0 -37
  200. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_regular.py +0 -88
  201. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_richclub.py +0 -149
  202. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_similarity.py +0 -1158
  203. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_simple_paths.py +0 -803
  204. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smallworld.py +0 -76
  205. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smetric.py +0 -8
  206. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_sparsifiers.py +0 -138
  207. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_structuralholes.py +0 -191
  208. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_summarization.py +0 -642
  209. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_swap.py +0 -179
  210. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_threshold.py +0 -270
  211. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_time_dependent.py +0 -431
  212. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_tournament.py +0 -161
  213. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_triads.py +0 -248
  214. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_vitality.py +0 -41
  215. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_voronoi.py +0 -103
  216. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_walks.py +0 -54
  217. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_wiener.py +0 -157
  218. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/__init__.py +0 -0
  219. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_beamsearch.py +0 -25
  220. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_bfs.py +0 -203
  221. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_dfs.py +0 -307
  222. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgebfs.py +0 -147
  223. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgedfs.py +0 -131
  224. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/__init__.py +0 -0
  225. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_branchings.py +0 -624
  226. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_coding.py +0 -114
  227. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_decomposition.py +0 -79
  228. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_distance_measures.py +0 -99
  229. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_mst.py +0 -934
  230. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_operations.py +0 -53
  231. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_recognition.py +0 -174
  232. package/runtime/python/okstra_vendor/networkx/classes/tests/__init__.py +0 -0
  233. package/runtime/python/okstra_vendor/networkx/classes/tests/dispatch_interface.py +0 -192
  234. package/runtime/python/okstra_vendor/networkx/classes/tests/historical_tests.py +0 -476
  235. package/runtime/python/okstra_vendor/networkx/classes/tests/test_coreviews.py +0 -362
  236. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph.py +0 -331
  237. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph_historical.py +0 -110
  238. package/runtime/python/okstra_vendor/networkx/classes/tests/test_filters.py +0 -177
  239. package/runtime/python/okstra_vendor/networkx/classes/tests/test_function.py +0 -1045
  240. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph.py +0 -950
  241. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph_historical.py +0 -12
  242. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graphviews.py +0 -349
  243. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multidigraph.py +0 -459
  244. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multigraph.py +0 -528
  245. package/runtime/python/okstra_vendor/networkx/classes/tests/test_reportviews.py +0 -1421
  246. package/runtime/python/okstra_vendor/networkx/classes/tests/test_special.py +0 -131
  247. package/runtime/python/okstra_vendor/networkx/classes/tests/test_subgraphviews.py +0 -371
  248. package/runtime/python/okstra_vendor/networkx/conftest.py +0 -261
  249. package/runtime/python/okstra_vendor/networkx/drawing/tests/__init__.py +0 -0
  250. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_complex.png +0 -0
  251. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_empty_graph.png +0 -0
  252. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_house_with_colors.png +0 -0
  253. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_labels_and_colors.png +0 -0
  254. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_shortest_path.png +0 -0
  255. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_house_with_colors.png +0 -0
  256. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_agraph.py +0 -237
  257. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_image_comparison_pylab_mpl.py +0 -229
  258. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_latex.py +0 -285
  259. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_layout.py +0 -631
  260. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pydot.py +0 -146
  261. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pylab.py +0 -1582
  262. package/runtime/python/okstra_vendor/networkx/generators/tests/__init__.py +0 -0
  263. package/runtime/python/okstra_vendor/networkx/generators/tests/test_atlas.py +0 -75
  264. package/runtime/python/okstra_vendor/networkx/generators/tests/test_classic.py +0 -642
  265. package/runtime/python/okstra_vendor/networkx/generators/tests/test_cographs.py +0 -20
  266. package/runtime/python/okstra_vendor/networkx/generators/tests/test_community.py +0 -362
  267. package/runtime/python/okstra_vendor/networkx/generators/tests/test_degree_seq.py +0 -224
  268. package/runtime/python/okstra_vendor/networkx/generators/tests/test_directed.py +0 -189
  269. package/runtime/python/okstra_vendor/networkx/generators/tests/test_duplication.py +0 -103
  270. package/runtime/python/okstra_vendor/networkx/generators/tests/test_ego.py +0 -39
  271. package/runtime/python/okstra_vendor/networkx/generators/tests/test_expanders.py +0 -182
  272. package/runtime/python/okstra_vendor/networkx/generators/tests/test_geometric.py +0 -488
  273. package/runtime/python/okstra_vendor/networkx/generators/tests/test_harary_graph.py +0 -133
  274. package/runtime/python/okstra_vendor/networkx/generators/tests/test_internet_as_graphs.py +0 -221
  275. package/runtime/python/okstra_vendor/networkx/generators/tests/test_intersection.py +0 -28
  276. package/runtime/python/okstra_vendor/networkx/generators/tests/test_interval_graph.py +0 -144
  277. package/runtime/python/okstra_vendor/networkx/generators/tests/test_joint_degree_seq.py +0 -125
  278. package/runtime/python/okstra_vendor/networkx/generators/tests/test_lattice.py +0 -264
  279. package/runtime/python/okstra_vendor/networkx/generators/tests/test_line.py +0 -316
  280. package/runtime/python/okstra_vendor/networkx/generators/tests/test_mycielski.py +0 -30
  281. package/runtime/python/okstra_vendor/networkx/generators/tests/test_nonisomorphic_trees.py +0 -82
  282. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_clustered.py +0 -33
  283. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_graphs.py +0 -495
  284. package/runtime/python/okstra_vendor/networkx/generators/tests/test_small.py +0 -220
  285. package/runtime/python/okstra_vendor/networkx/generators/tests/test_spectral_graph_forge.py +0 -49
  286. package/runtime/python/okstra_vendor/networkx/generators/tests/test_stochastic.py +0 -72
  287. package/runtime/python/okstra_vendor/networkx/generators/tests/test_sudoku.py +0 -92
  288. package/runtime/python/okstra_vendor/networkx/generators/tests/test_time_series.py +0 -64
  289. package/runtime/python/okstra_vendor/networkx/generators/tests/test_trees.py +0 -195
  290. package/runtime/python/okstra_vendor/networkx/generators/tests/test_triads.py +0 -15
  291. package/runtime/python/okstra_vendor/networkx/linalg/tests/__init__.py +0 -0
  292. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_algebraic_connectivity.py +0 -400
  293. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_attrmatrix.py +0 -108
  294. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_bethehessian.py +0 -40
  295. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_graphmatrix.py +0 -275
  296. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_laplacian.py +0 -334
  297. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_modularity.py +0 -86
  298. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_spectrum.py +0 -70
  299. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/__init__.py +0 -0
  300. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_adjacency.py +0 -78
  301. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_cytoscape.py +0 -78
  302. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_node_link.py +0 -109
  303. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_tree.py +0 -48
  304. package/runtime/python/okstra_vendor/networkx/readwrite/tests/__init__.py +0 -0
  305. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_adjlist.py +0 -354
  306. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_edgelist.py +0 -318
  307. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gexf.py +0 -612
  308. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gml.py +0 -744
  309. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graph6.py +0 -181
  310. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graphml.py +0 -1531
  311. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_leda.py +0 -30
  312. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_p2g.py +0 -63
  313. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_pajek.py +0 -128
  314. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_sparse6.py +0 -166
  315. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_text.py +0 -1742
  316. package/runtime/python/okstra_vendor/networkx/tests/__init__.py +0 -0
  317. package/runtime/python/okstra_vendor/networkx/tests/test_all_random_functions.py +0 -248
  318. package/runtime/python/okstra_vendor/networkx/tests/test_convert.py +0 -321
  319. package/runtime/python/okstra_vendor/networkx/tests/test_convert_numpy.py +0 -531
  320. package/runtime/python/okstra_vendor/networkx/tests/test_convert_pandas.py +0 -349
  321. package/runtime/python/okstra_vendor/networkx/tests/test_convert_scipy.py +0 -281
  322. package/runtime/python/okstra_vendor/networkx/tests/test_exceptions.py +0 -40
  323. package/runtime/python/okstra_vendor/networkx/tests/test_import.py +0 -11
  324. package/runtime/python/okstra_vendor/networkx/tests/test_lazy_imports.py +0 -96
  325. package/runtime/python/okstra_vendor/networkx/tests/test_relabel.py +0 -349
  326. package/runtime/python/okstra_vendor/networkx/tests/test_removed_functions_exception_messages.py +0 -8
  327. package/runtime/python/okstra_vendor/networkx/utils/tests/__init__.py +0 -0
  328. package/runtime/python/okstra_vendor/networkx/utils/tests/test__init.py +0 -11
  329. package/runtime/python/okstra_vendor/networkx/utils/tests/test_backends.py +0 -225
  330. package/runtime/python/okstra_vendor/networkx/utils/tests/test_config.py +0 -263
  331. package/runtime/python/okstra_vendor/networkx/utils/tests/test_decorators.py +0 -510
  332. package/runtime/python/okstra_vendor/networkx/utils/tests/test_heaps.py +0 -131
  333. package/runtime/python/okstra_vendor/networkx/utils/tests/test_mapped_queue.py +0 -268
  334. package/runtime/python/okstra_vendor/networkx/utils/tests/test_misc.py +0 -393
  335. package/runtime/python/okstra_vendor/networkx/utils/tests/test_random_sequence.py +0 -53
  336. package/runtime/python/okstra_vendor/networkx/utils/tests/test_rcm.py +0 -63
  337. 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"