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