okstra 0.108.0 → 0.110.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 (648) hide show
  1. package/README.kr.md +1 -1
  2. package/README.md +1 -1
  3. package/docs/kr/architecture.md +3 -3
  4. package/docs/kr/cli.md +2 -2
  5. package/docs/project-structure-overview.md +3 -3
  6. package/package.json +1 -1
  7. package/runtime/BUILD.json +2 -2
  8. package/runtime/prompts/profiles/_common-contract.md +1 -1
  9. package/runtime/python/okstra_ctl/graphify_cmd.py +225 -0
  10. package/runtime/python/okstra_ctl/resolve_task_key.py +15 -9
  11. package/runtime/python/okstra_ctl/worktree.py +14 -13
  12. package/runtime/python/okstra_project/__init__.py +2 -0
  13. package/runtime/python/okstra_project/state.py +36 -0
  14. package/runtime/python/okstra_vendor/__init__.py +41 -0
  15. package/runtime/python/okstra_vendor/graphify/.vendored-version +1 -0
  16. package/runtime/python/okstra_vendor/graphify/__init__.py +28 -0
  17. package/runtime/python/okstra_vendor/graphify/__main__.py +1371 -0
  18. package/runtime/python/okstra_vendor/graphify/analyze.py +540 -0
  19. package/runtime/python/okstra_vendor/graphify/benchmark.py +129 -0
  20. package/runtime/python/okstra_vendor/graphify/build.py +107 -0
  21. package/runtime/python/okstra_vendor/graphify/cache.py +169 -0
  22. package/runtime/python/okstra_vendor/graphify/cluster.py +137 -0
  23. package/runtime/python/okstra_vendor/graphify/detect.py +510 -0
  24. package/runtime/python/okstra_vendor/graphify/export.py +1014 -0
  25. package/runtime/python/okstra_vendor/graphify/extract.py +3277 -0
  26. package/runtime/python/okstra_vendor/graphify/hooks.py +220 -0
  27. package/runtime/python/okstra_vendor/graphify/ingest.py +297 -0
  28. package/runtime/python/okstra_vendor/graphify/manifest.py +4 -0
  29. package/runtime/python/okstra_vendor/graphify/report.py +175 -0
  30. package/runtime/python/okstra_vendor/graphify/security.py +203 -0
  31. package/runtime/python/okstra_vendor/graphify/serve.py +373 -0
  32. package/runtime/python/okstra_vendor/graphify/skill-aider.md +1184 -0
  33. package/runtime/python/okstra_vendor/graphify/skill-claw.md +1184 -0
  34. package/runtime/python/okstra_vendor/graphify/skill-codex.md +1242 -0
  35. package/runtime/python/okstra_vendor/graphify/skill-copilot.md +1268 -0
  36. package/runtime/python/okstra_vendor/graphify/skill-droid.md +1239 -0
  37. package/runtime/python/okstra_vendor/graphify/skill-kiro.md +1183 -0
  38. package/runtime/python/okstra_vendor/graphify/skill-opencode.md +1238 -0
  39. package/runtime/python/okstra_vendor/graphify/skill-trae.md +1208 -0
  40. package/runtime/python/okstra_vendor/graphify/skill-vscode.md +253 -0
  41. package/runtime/python/okstra_vendor/graphify/skill-windows.md +1245 -0
  42. package/runtime/python/okstra_vendor/graphify/skill.md +1319 -0
  43. package/runtime/python/okstra_vendor/graphify/transcribe.py +182 -0
  44. package/runtime/python/okstra_vendor/graphify/validate.py +72 -0
  45. package/runtime/python/okstra_vendor/graphify/watch.py +188 -0
  46. package/runtime/python/okstra_vendor/graphify/wiki.py +214 -0
  47. package/runtime/python/okstra_vendor/networkx/__init__.py +62 -0
  48. package/runtime/python/okstra_vendor/networkx/algorithms/__init__.py +134 -0
  49. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/__init__.py +26 -0
  50. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/clique.py +259 -0
  51. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/clustering_coefficient.py +71 -0
  52. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/connectivity.py +412 -0
  53. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/density.py +396 -0
  54. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/distance_measures.py +150 -0
  55. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/dominating_set.py +149 -0
  56. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/kcomponents.py +369 -0
  57. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/matching.py +44 -0
  58. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/maxcut.py +143 -0
  59. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/ramsey.py +53 -0
  60. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/steinertree.py +265 -0
  61. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/__init__.py +0 -0
  62. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +41 -0
  63. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_clique.py +112 -0
  64. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_connectivity.py +199 -0
  65. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_density.py +146 -0
  66. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_distance_measures.py +59 -0
  67. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_dominating_set.py +78 -0
  68. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_kcomponents.py +303 -0
  69. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_matching.py +8 -0
  70. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_maxcut.py +94 -0
  71. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_ramsey.py +31 -0
  72. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_steinertree.py +306 -0
  73. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_traveling_salesman.py +1014 -0
  74. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_treewidth.py +274 -0
  75. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_vertex_cover.py +68 -0
  76. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/traveling_salesman.py +1508 -0
  77. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/treewidth.py +255 -0
  78. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/vertex_cover.py +83 -0
  79. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/__init__.py +5 -0
  80. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/connectivity.py +122 -0
  81. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/correlation.py +302 -0
  82. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/mixing.py +255 -0
  83. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/neighbor_degree.py +160 -0
  84. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/pairs.py +127 -0
  85. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/__init__.py +0 -0
  86. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/base_test.py +81 -0
  87. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_connectivity.py +143 -0
  88. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_correlation.py +122 -0
  89. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_mixing.py +174 -0
  90. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_neighbor_degree.py +107 -0
  91. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_pairs.py +87 -0
  92. package/runtime/python/okstra_vendor/networkx/algorithms/asteroidal.py +164 -0
  93. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/__init__.py +88 -0
  94. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/basic.py +322 -0
  95. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/centrality.py +290 -0
  96. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/cluster.py +289 -0
  97. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/covering.py +57 -0
  98. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/edgelist.py +360 -0
  99. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/extendability.py +105 -0
  100. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/generators.py +603 -0
  101. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/link_analysis.py +316 -0
  102. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/matching.py +590 -0
  103. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/matrix.py +232 -0
  104. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/projection.py +526 -0
  105. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/redundancy.py +112 -0
  106. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/spectral.py +69 -0
  107. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/__init__.py +0 -0
  108. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_basic.py +125 -0
  109. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_centrality.py +192 -0
  110. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_cluster.py +84 -0
  111. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_covering.py +33 -0
  112. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_edgelist.py +240 -0
  113. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_extendability.py +334 -0
  114. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_generators.py +407 -0
  115. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_link_analysis.py +218 -0
  116. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matching.py +327 -0
  117. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matrix.py +138 -0
  118. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_project.py +409 -0
  119. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_redundancy.py +35 -0
  120. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py +80 -0
  121. package/runtime/python/okstra_vendor/networkx/algorithms/boundary.py +168 -0
  122. package/runtime/python/okstra_vendor/networkx/algorithms/bridges.py +205 -0
  123. package/runtime/python/okstra_vendor/networkx/algorithms/broadcasting.py +164 -0
  124. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/__init__.py +20 -0
  125. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/betweenness.py +591 -0
  126. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/betweenness_subset.py +236 -0
  127. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/closeness.py +282 -0
  128. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_betweenness.py +364 -0
  129. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_betweenness_subset.py +227 -0
  130. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/current_flow_closeness.py +96 -0
  131. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/degree_alg.py +150 -0
  132. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/dispersion.py +107 -0
  133. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/eigenvector.py +357 -0
  134. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/flow_matrix.py +130 -0
  135. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/group.py +787 -0
  136. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/harmonic.py +88 -0
  137. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/katz.py +331 -0
  138. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/laplacian.py +150 -0
  139. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/load.py +200 -0
  140. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/percolation.py +128 -0
  141. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/reaching.py +209 -0
  142. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/second_order.py +141 -0
  143. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/subgraph_alg.py +361 -0
  144. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/__init__.py +0 -0
  145. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality.py +923 -0
  146. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py +354 -0
  147. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_closeness_centrality.py +274 -0
  148. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py +259 -0
  149. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py +147 -0
  150. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_closeness.py +43 -0
  151. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_degree_centrality.py +144 -0
  152. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_dispersion.py +73 -0
  153. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py +186 -0
  154. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_group.py +277 -0
  155. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_harmonic_centrality.py +122 -0
  156. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_katz_centrality.py +345 -0
  157. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_laplacian_centrality.py +220 -0
  158. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_load_centrality.py +344 -0
  159. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_percolation_centrality.py +87 -0
  160. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_reaching.py +140 -0
  161. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_second_order_centrality.py +82 -0
  162. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_subgraph.py +110 -0
  163. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_trophic.py +302 -0
  164. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_voterank.py +64 -0
  165. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/trophic.py +181 -0
  166. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/voterank_alg.py +95 -0
  167. package/runtime/python/okstra_vendor/networkx/algorithms/chains.py +172 -0
  168. package/runtime/python/okstra_vendor/networkx/algorithms/chordal.py +443 -0
  169. package/runtime/python/okstra_vendor/networkx/algorithms/clique.py +818 -0
  170. package/runtime/python/okstra_vendor/networkx/algorithms/cluster.py +732 -0
  171. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/__init__.py +4 -0
  172. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/equitable_coloring.py +505 -0
  173. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/greedy_coloring.py +565 -0
  174. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/__init__.py +0 -0
  175. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/test_coloring.py +863 -0
  176. package/runtime/python/okstra_vendor/networkx/algorithms/communicability_alg.py +163 -0
  177. package/runtime/python/okstra_vendor/networkx/algorithms/community/__init__.py +28 -0
  178. package/runtime/python/okstra_vendor/networkx/algorithms/community/asyn_fluid.py +153 -0
  179. package/runtime/python/okstra_vendor/networkx/algorithms/community/bipartitions.py +354 -0
  180. package/runtime/python/okstra_vendor/networkx/algorithms/community/centrality.py +171 -0
  181. package/runtime/python/okstra_vendor/networkx/algorithms/community/community_utils.py +30 -0
  182. package/runtime/python/okstra_vendor/networkx/algorithms/community/divisive.py +216 -0
  183. package/runtime/python/okstra_vendor/networkx/algorithms/community/kclique.py +79 -0
  184. package/runtime/python/okstra_vendor/networkx/algorithms/community/label_propagation.py +338 -0
  185. package/runtime/python/okstra_vendor/networkx/algorithms/community/leiden.py +162 -0
  186. package/runtime/python/okstra_vendor/networkx/algorithms/community/local.py +220 -0
  187. package/runtime/python/okstra_vendor/networkx/algorithms/community/louvain.py +384 -0
  188. package/runtime/python/okstra_vendor/networkx/algorithms/community/lukes.py +227 -0
  189. package/runtime/python/okstra_vendor/networkx/algorithms/community/modularity_max.py +452 -0
  190. package/runtime/python/okstra_vendor/networkx/algorithms/community/quality.py +347 -0
  191. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/__init__.py +0 -0
  192. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_asyn_fluid.py +147 -0
  193. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_bipartitions.py +157 -0
  194. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_centrality.py +85 -0
  195. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_divisive.py +106 -0
  196. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_kclique.py +91 -0
  197. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_label_propagation.py +241 -0
  198. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_leiden.py +138 -0
  199. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_local.py +76 -0
  200. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_louvain.py +264 -0
  201. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_lukes.py +152 -0
  202. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_modularity_max.py +340 -0
  203. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_quality.py +139 -0
  204. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_utils.py +26 -0
  205. package/runtime/python/okstra_vendor/networkx/algorithms/components/__init__.py +6 -0
  206. package/runtime/python/okstra_vendor/networkx/algorithms/components/attracting.py +115 -0
  207. package/runtime/python/okstra_vendor/networkx/algorithms/components/biconnected.py +394 -0
  208. package/runtime/python/okstra_vendor/networkx/algorithms/components/connected.py +282 -0
  209. package/runtime/python/okstra_vendor/networkx/algorithms/components/semiconnected.py +71 -0
  210. package/runtime/python/okstra_vendor/networkx/algorithms/components/strongly_connected.py +359 -0
  211. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/__init__.py +0 -0
  212. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_attracting.py +70 -0
  213. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_biconnected.py +248 -0
  214. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_connected.py +138 -0
  215. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_semiconnected.py +55 -0
  216. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_strongly_connected.py +193 -0
  217. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_weakly_connected.py +96 -0
  218. package/runtime/python/okstra_vendor/networkx/algorithms/components/weakly_connected.py +196 -0
  219. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/__init__.py +11 -0
  220. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/connectivity.py +811 -0
  221. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/cuts.py +616 -0
  222. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/disjoint_paths.py +408 -0
  223. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/edge_augmentation.py +1270 -0
  224. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/edge_kcomponents.py +592 -0
  225. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/kcomponents.py +220 -0
  226. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/kcutsets.py +235 -0
  227. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/stoerwagner.py +152 -0
  228. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/__init__.py +0 -0
  229. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_connectivity.py +421 -0
  230. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_cuts.py +309 -0
  231. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_disjoint_paths.py +249 -0
  232. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +502 -0
  233. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +488 -0
  234. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcomponents.py +323 -0
  235. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcutsets.py +280 -0
  236. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_stoer_wagner.py +102 -0
  237. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/utils.py +88 -0
  238. package/runtime/python/okstra_vendor/networkx/algorithms/core.py +588 -0
  239. package/runtime/python/okstra_vendor/networkx/algorithms/covering.py +142 -0
  240. package/runtime/python/okstra_vendor/networkx/algorithms/cuts.py +416 -0
  241. package/runtime/python/okstra_vendor/networkx/algorithms/cycles.py +1234 -0
  242. package/runtime/python/okstra_vendor/networkx/algorithms/d_separation.py +677 -0
  243. package/runtime/python/okstra_vendor/networkx/algorithms/dag.py +1392 -0
  244. package/runtime/python/okstra_vendor/networkx/algorithms/distance_measures.py +1095 -0
  245. package/runtime/python/okstra_vendor/networkx/algorithms/distance_regular.py +272 -0
  246. package/runtime/python/okstra_vendor/networkx/algorithms/dominance.py +142 -0
  247. package/runtime/python/okstra_vendor/networkx/algorithms/dominating.py +268 -0
  248. package/runtime/python/okstra_vendor/networkx/algorithms/efficiency_measures.py +167 -0
  249. package/runtime/python/okstra_vendor/networkx/algorithms/euler.py +470 -0
  250. package/runtime/python/okstra_vendor/networkx/algorithms/flow/__init__.py +11 -0
  251. package/runtime/python/okstra_vendor/networkx/algorithms/flow/boykovkolmogorov.py +370 -0
  252. package/runtime/python/okstra_vendor/networkx/algorithms/flow/capacityscaling.py +407 -0
  253. package/runtime/python/okstra_vendor/networkx/algorithms/flow/dinitz_alg.py +238 -0
  254. package/runtime/python/okstra_vendor/networkx/algorithms/flow/edmondskarp.py +241 -0
  255. package/runtime/python/okstra_vendor/networkx/algorithms/flow/gomory_hu.py +178 -0
  256. package/runtime/python/okstra_vendor/networkx/algorithms/flow/maxflow.py +611 -0
  257. package/runtime/python/okstra_vendor/networkx/algorithms/flow/mincost.py +356 -0
  258. package/runtime/python/okstra_vendor/networkx/algorithms/flow/networksimplex.py +662 -0
  259. package/runtime/python/okstra_vendor/networkx/algorithms/flow/preflowpush.py +425 -0
  260. package/runtime/python/okstra_vendor/networkx/algorithms/flow/shortestaugmentingpath.py +300 -0
  261. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/__init__.py +0 -0
  262. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gl1.gpickle.bz2 +0 -0
  263. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gw1.gpickle.bz2 +0 -0
  264. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/netgen-2.gpickle.bz2 +0 -0
  265. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_gomory_hu.py +128 -0
  266. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow.py +573 -0
  267. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +155 -0
  268. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_mincost.py +475 -0
  269. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_networksimplex.py +481 -0
  270. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/wlm3.gpickle.bz2 +0 -0
  271. package/runtime/python/okstra_vendor/networkx/algorithms/flow/utils.py +194 -0
  272. package/runtime/python/okstra_vendor/networkx/algorithms/graph_hashing.py +435 -0
  273. package/runtime/python/okstra_vendor/networkx/algorithms/graphical.py +483 -0
  274. package/runtime/python/okstra_vendor/networkx/algorithms/hierarchy.py +57 -0
  275. package/runtime/python/okstra_vendor/networkx/algorithms/hybrid.py +196 -0
  276. package/runtime/python/okstra_vendor/networkx/algorithms/isolate.py +107 -0
  277. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/__init__.py +7 -0
  278. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/ismags.py +1306 -0
  279. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/isomorph.py +336 -0
  280. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/isomorphvf2.py +1262 -0
  281. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/matchhelpers.py +352 -0
  282. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/temporalisomorphvf2.py +308 -0
  283. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/__init__.py +0 -0
  284. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.A99 +0 -0
  285. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.B99 +0 -0
  286. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.A99 +0 -0
  287. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.B99 +0 -0
  288. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_ismags.py +719 -0
  289. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphism.py +103 -0
  290. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphvf2.py +490 -0
  291. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_match_helpers.py +64 -0
  292. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py +212 -0
  293. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py +202 -0
  294. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp.py +1655 -0
  295. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py +3118 -0
  296. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2userfunc.py +196 -0
  297. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tree_isomorphism.py +264 -0
  298. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/vf2pp.py +1102 -0
  299. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/vf2userfunc.py +192 -0
  300. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/__init__.py +2 -0
  301. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/hits_alg.py +337 -0
  302. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/pagerank_alg.py +498 -0
  303. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/__init__.py +0 -0
  304. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_hits.py +77 -0
  305. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_pagerank.py +213 -0
  306. package/runtime/python/okstra_vendor/networkx/algorithms/link_prediction.py +687 -0
  307. package/runtime/python/okstra_vendor/networkx/algorithms/lowest_common_ancestors.py +280 -0
  308. package/runtime/python/okstra_vendor/networkx/algorithms/matching.py +1148 -0
  309. package/runtime/python/okstra_vendor/networkx/algorithms/minors/__init__.py +27 -0
  310. package/runtime/python/okstra_vendor/networkx/algorithms/minors/contraction.py +738 -0
  311. package/runtime/python/okstra_vendor/networkx/algorithms/minors/tests/test_contraction.py +544 -0
  312. package/runtime/python/okstra_vendor/networkx/algorithms/mis.py +78 -0
  313. package/runtime/python/okstra_vendor/networkx/algorithms/moral.py +59 -0
  314. package/runtime/python/okstra_vendor/networkx/algorithms/node_classification.py +219 -0
  315. package/runtime/python/okstra_vendor/networkx/algorithms/non_randomness.py +155 -0
  316. package/runtime/python/okstra_vendor/networkx/algorithms/operators/__init__.py +4 -0
  317. package/runtime/python/okstra_vendor/networkx/algorithms/operators/all.py +324 -0
  318. package/runtime/python/okstra_vendor/networkx/algorithms/operators/binary.py +468 -0
  319. package/runtime/python/okstra_vendor/networkx/algorithms/operators/product.py +633 -0
  320. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/__init__.py +0 -0
  321. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_all.py +328 -0
  322. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_binary.py +451 -0
  323. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_product.py +491 -0
  324. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_unary.py +55 -0
  325. package/runtime/python/okstra_vendor/networkx/algorithms/operators/unary.py +77 -0
  326. package/runtime/python/okstra_vendor/networkx/algorithms/perfect_graph.py +73 -0
  327. package/runtime/python/okstra_vendor/networkx/algorithms/planar_drawing.py +464 -0
  328. package/runtime/python/okstra_vendor/networkx/algorithms/planarity.py +1463 -0
  329. package/runtime/python/okstra_vendor/networkx/algorithms/polynomials.py +306 -0
  330. package/runtime/python/okstra_vendor/networkx/algorithms/reciprocity.py +98 -0
  331. package/runtime/python/okstra_vendor/networkx/algorithms/regular.py +167 -0
  332. package/runtime/python/okstra_vendor/networkx/algorithms/richclub.py +138 -0
  333. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/__init__.py +5 -0
  334. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/astar.py +239 -0
  335. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/dense.py +264 -0
  336. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/generic.py +716 -0
  337. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/__init__.py +0 -0
  338. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_astar.py +254 -0
  339. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense.py +212 -0
  340. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +88 -0
  341. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_generic.py +511 -0
  342. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_unweighted.py +149 -0
  343. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_weighted.py +983 -0
  344. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/unweighted.py +625 -0
  345. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/weighted.py +2542 -0
  346. package/runtime/python/okstra_vendor/networkx/algorithms/similarity.py +2107 -0
  347. package/runtime/python/okstra_vendor/networkx/algorithms/simple_paths.py +966 -0
  348. package/runtime/python/okstra_vendor/networkx/algorithms/smallworld.py +404 -0
  349. package/runtime/python/okstra_vendor/networkx/algorithms/smetric.py +30 -0
  350. package/runtime/python/okstra_vendor/networkx/algorithms/sparsifiers.py +296 -0
  351. package/runtime/python/okstra_vendor/networkx/algorithms/structuralholes.py +374 -0
  352. package/runtime/python/okstra_vendor/networkx/algorithms/summarization.py +564 -0
  353. package/runtime/python/okstra_vendor/networkx/algorithms/swap.py +406 -0
  354. package/runtime/python/okstra_vendor/networkx/algorithms/tests/__init__.py +0 -0
  355. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_asteroidal.py +23 -0
  356. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_boundary.py +154 -0
  357. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_bridges.py +144 -0
  358. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_broadcasting.py +109 -0
  359. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chains.py +136 -0
  360. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chordal.py +129 -0
  361. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_clique.py +300 -0
  362. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cluster.py +678 -0
  363. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_communicability.py +80 -0
  364. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_core.py +266 -0
  365. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_covering.py +85 -0
  366. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cuts.py +171 -0
  367. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cycles.py +984 -0
  368. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_d_separation.py +340 -0
  369. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dag.py +835 -0
  370. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_measures.py +831 -0
  371. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_regular.py +85 -0
  372. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominance.py +299 -0
  373. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominating.py +115 -0
  374. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_efficiency.py +58 -0
  375. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_euler.py +314 -0
  376. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graph_hashing.py +872 -0
  377. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graphical.py +163 -0
  378. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hierarchy.py +46 -0
  379. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hybrid.py +24 -0
  380. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_isolate.py +26 -0
  381. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_link_prediction.py +615 -0
  382. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_lowest_common_ancestors.py +459 -0
  383. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_matching.py +556 -0
  384. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_max_weight_clique.py +179 -0
  385. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_mis.py +62 -0
  386. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_moral.py +15 -0
  387. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_node_classification.py +140 -0
  388. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_non_randomness.py +60 -0
  389. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_perfect_graph.py +27 -0
  390. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planar_drawing.py +274 -0
  391. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planarity.py +556 -0
  392. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_polynomials.py +57 -0
  393. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_reciprocity.py +37 -0
  394. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_regular.py +88 -0
  395. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_richclub.py +149 -0
  396. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_similarity.py +1158 -0
  397. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_simple_paths.py +803 -0
  398. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smallworld.py +76 -0
  399. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smetric.py +8 -0
  400. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_sparsifiers.py +138 -0
  401. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_structuralholes.py +191 -0
  402. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_summarization.py +642 -0
  403. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_swap.py +179 -0
  404. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_threshold.py +270 -0
  405. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_time_dependent.py +431 -0
  406. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_tournament.py +161 -0
  407. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_triads.py +248 -0
  408. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_vitality.py +41 -0
  409. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_voronoi.py +103 -0
  410. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_walks.py +54 -0
  411. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_wiener.py +157 -0
  412. package/runtime/python/okstra_vendor/networkx/algorithms/threshold.py +981 -0
  413. package/runtime/python/okstra_vendor/networkx/algorithms/time_dependent.py +142 -0
  414. package/runtime/python/okstra_vendor/networkx/algorithms/tournament.py +406 -0
  415. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/__init__.py +5 -0
  416. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/beamsearch.py +90 -0
  417. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/breadth_first_search.py +576 -0
  418. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/depth_first_search.py +529 -0
  419. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/edgebfs.py +185 -0
  420. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/edgedfs.py +182 -0
  421. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/__init__.py +0 -0
  422. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_beamsearch.py +25 -0
  423. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_bfs.py +203 -0
  424. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_dfs.py +307 -0
  425. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgebfs.py +147 -0
  426. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgedfs.py +131 -0
  427. package/runtime/python/okstra_vendor/networkx/algorithms/tree/__init__.py +7 -0
  428. package/runtime/python/okstra_vendor/networkx/algorithms/tree/branchings.py +1042 -0
  429. package/runtime/python/okstra_vendor/networkx/algorithms/tree/coding.py +413 -0
  430. package/runtime/python/okstra_vendor/networkx/algorithms/tree/decomposition.py +88 -0
  431. package/runtime/python/okstra_vendor/networkx/algorithms/tree/distance_measures.py +219 -0
  432. package/runtime/python/okstra_vendor/networkx/algorithms/tree/mst.py +1281 -0
  433. package/runtime/python/okstra_vendor/networkx/algorithms/tree/operations.py +106 -0
  434. package/runtime/python/okstra_vendor/networkx/algorithms/tree/recognition.py +273 -0
  435. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/__init__.py +0 -0
  436. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_branchings.py +624 -0
  437. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_coding.py +114 -0
  438. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_decomposition.py +79 -0
  439. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_distance_measures.py +99 -0
  440. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_mst.py +934 -0
  441. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_operations.py +53 -0
  442. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_recognition.py +174 -0
  443. package/runtime/python/okstra_vendor/networkx/algorithms/triads.py +500 -0
  444. package/runtime/python/okstra_vendor/networkx/algorithms/vitality.py +76 -0
  445. package/runtime/python/okstra_vendor/networkx/algorithms/voronoi.py +86 -0
  446. package/runtime/python/okstra_vendor/networkx/algorithms/walks.py +77 -0
  447. package/runtime/python/okstra_vendor/networkx/algorithms/wiener.py +278 -0
  448. package/runtime/python/okstra_vendor/networkx/classes/__init__.py +13 -0
  449. package/runtime/python/okstra_vendor/networkx/classes/coreviews.py +435 -0
  450. package/runtime/python/okstra_vendor/networkx/classes/digraph.py +1363 -0
  451. package/runtime/python/okstra_vendor/networkx/classes/filters.py +95 -0
  452. package/runtime/python/okstra_vendor/networkx/classes/function.py +1549 -0
  453. package/runtime/python/okstra_vendor/networkx/classes/graph.py +2082 -0
  454. package/runtime/python/okstra_vendor/networkx/classes/graphviews.py +269 -0
  455. package/runtime/python/okstra_vendor/networkx/classes/multidigraph.py +977 -0
  456. package/runtime/python/okstra_vendor/networkx/classes/multigraph.py +1294 -0
  457. package/runtime/python/okstra_vendor/networkx/classes/reportviews.py +1447 -0
  458. package/runtime/python/okstra_vendor/networkx/classes/tests/__init__.py +0 -0
  459. package/runtime/python/okstra_vendor/networkx/classes/tests/dispatch_interface.py +192 -0
  460. package/runtime/python/okstra_vendor/networkx/classes/tests/historical_tests.py +476 -0
  461. package/runtime/python/okstra_vendor/networkx/classes/tests/test_coreviews.py +362 -0
  462. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph.py +331 -0
  463. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph_historical.py +110 -0
  464. package/runtime/python/okstra_vendor/networkx/classes/tests/test_filters.py +177 -0
  465. package/runtime/python/okstra_vendor/networkx/classes/tests/test_function.py +1045 -0
  466. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph.py +950 -0
  467. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph_historical.py +12 -0
  468. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graphviews.py +349 -0
  469. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multidigraph.py +459 -0
  470. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multigraph.py +528 -0
  471. package/runtime/python/okstra_vendor/networkx/classes/tests/test_reportviews.py +1421 -0
  472. package/runtime/python/okstra_vendor/networkx/classes/tests/test_special.py +131 -0
  473. package/runtime/python/okstra_vendor/networkx/classes/tests/test_subgraphviews.py +371 -0
  474. package/runtime/python/okstra_vendor/networkx/conftest.py +261 -0
  475. package/runtime/python/okstra_vendor/networkx/convert.py +502 -0
  476. package/runtime/python/okstra_vendor/networkx/convert_matrix.py +1314 -0
  477. package/runtime/python/okstra_vendor/networkx/drawing/__init__.py +7 -0
  478. package/runtime/python/okstra_vendor/networkx/drawing/layout.py +2036 -0
  479. package/runtime/python/okstra_vendor/networkx/drawing/nx_agraph.py +470 -0
  480. package/runtime/python/okstra_vendor/networkx/drawing/nx_latex.py +570 -0
  481. package/runtime/python/okstra_vendor/networkx/drawing/nx_pydot.py +361 -0
  482. package/runtime/python/okstra_vendor/networkx/drawing/nx_pylab.py +2978 -0
  483. package/runtime/python/okstra_vendor/networkx/drawing/tests/__init__.py +0 -0
  484. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_complex.png +0 -0
  485. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_empty_graph.png +0 -0
  486. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_house_with_colors.png +0 -0
  487. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_labels_and_colors.png +0 -0
  488. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_shortest_path.png +0 -0
  489. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_house_with_colors.png +0 -0
  490. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_agraph.py +237 -0
  491. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_image_comparison_pylab_mpl.py +229 -0
  492. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_latex.py +285 -0
  493. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_layout.py +631 -0
  494. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pydot.py +146 -0
  495. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pylab.py +1582 -0
  496. package/runtime/python/okstra_vendor/networkx/exception.py +131 -0
  497. package/runtime/python/okstra_vendor/networkx/generators/__init__.py +34 -0
  498. package/runtime/python/okstra_vendor/networkx/generators/atlas.dat.gz +0 -0
  499. package/runtime/python/okstra_vendor/networkx/generators/atlas.py +227 -0
  500. package/runtime/python/okstra_vendor/networkx/generators/classic.py +1091 -0
  501. package/runtime/python/okstra_vendor/networkx/generators/cographs.py +68 -0
  502. package/runtime/python/okstra_vendor/networkx/generators/community.py +1070 -0
  503. package/runtime/python/okstra_vendor/networkx/generators/degree_seq.py +886 -0
  504. package/runtime/python/okstra_vendor/networkx/generators/directed.py +572 -0
  505. package/runtime/python/okstra_vendor/networkx/generators/duplication.py +174 -0
  506. package/runtime/python/okstra_vendor/networkx/generators/ego.py +66 -0
  507. package/runtime/python/okstra_vendor/networkx/generators/expanders.py +499 -0
  508. package/runtime/python/okstra_vendor/networkx/generators/geometric.py +1037 -0
  509. package/runtime/python/okstra_vendor/networkx/generators/harary_graph.py +163 -0
  510. package/runtime/python/okstra_vendor/networkx/generators/internet_as_graphs.py +443 -0
  511. package/runtime/python/okstra_vendor/networkx/generators/intersection.py +125 -0
  512. package/runtime/python/okstra_vendor/networkx/generators/interval_graph.py +70 -0
  513. package/runtime/python/okstra_vendor/networkx/generators/joint_degree_seq.py +664 -0
  514. package/runtime/python/okstra_vendor/networkx/generators/lattice.py +405 -0
  515. package/runtime/python/okstra_vendor/networkx/generators/line.py +501 -0
  516. package/runtime/python/okstra_vendor/networkx/generators/mycielski.py +110 -0
  517. package/runtime/python/okstra_vendor/networkx/generators/nonisomorphic_trees.py +259 -0
  518. package/runtime/python/okstra_vendor/networkx/generators/random_clustered.py +117 -0
  519. package/runtime/python/okstra_vendor/networkx/generators/random_graphs.py +1416 -0
  520. package/runtime/python/okstra_vendor/networkx/generators/small.py +1070 -0
  521. package/runtime/python/okstra_vendor/networkx/generators/social.py +554 -0
  522. package/runtime/python/okstra_vendor/networkx/generators/spectral_graph_forge.py +120 -0
  523. package/runtime/python/okstra_vendor/networkx/generators/stochastic.py +54 -0
  524. package/runtime/python/okstra_vendor/networkx/generators/sudoku.py +131 -0
  525. package/runtime/python/okstra_vendor/networkx/generators/tests/__init__.py +0 -0
  526. package/runtime/python/okstra_vendor/networkx/generators/tests/test_atlas.py +75 -0
  527. package/runtime/python/okstra_vendor/networkx/generators/tests/test_classic.py +642 -0
  528. package/runtime/python/okstra_vendor/networkx/generators/tests/test_cographs.py +20 -0
  529. package/runtime/python/okstra_vendor/networkx/generators/tests/test_community.py +362 -0
  530. package/runtime/python/okstra_vendor/networkx/generators/tests/test_degree_seq.py +224 -0
  531. package/runtime/python/okstra_vendor/networkx/generators/tests/test_directed.py +189 -0
  532. package/runtime/python/okstra_vendor/networkx/generators/tests/test_duplication.py +103 -0
  533. package/runtime/python/okstra_vendor/networkx/generators/tests/test_ego.py +39 -0
  534. package/runtime/python/okstra_vendor/networkx/generators/tests/test_expanders.py +182 -0
  535. package/runtime/python/okstra_vendor/networkx/generators/tests/test_geometric.py +488 -0
  536. package/runtime/python/okstra_vendor/networkx/generators/tests/test_harary_graph.py +133 -0
  537. package/runtime/python/okstra_vendor/networkx/generators/tests/test_internet_as_graphs.py +221 -0
  538. package/runtime/python/okstra_vendor/networkx/generators/tests/test_intersection.py +28 -0
  539. package/runtime/python/okstra_vendor/networkx/generators/tests/test_interval_graph.py +144 -0
  540. package/runtime/python/okstra_vendor/networkx/generators/tests/test_joint_degree_seq.py +125 -0
  541. package/runtime/python/okstra_vendor/networkx/generators/tests/test_lattice.py +264 -0
  542. package/runtime/python/okstra_vendor/networkx/generators/tests/test_line.py +316 -0
  543. package/runtime/python/okstra_vendor/networkx/generators/tests/test_mycielski.py +30 -0
  544. package/runtime/python/okstra_vendor/networkx/generators/tests/test_nonisomorphic_trees.py +82 -0
  545. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_clustered.py +33 -0
  546. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_graphs.py +495 -0
  547. package/runtime/python/okstra_vendor/networkx/generators/tests/test_small.py +220 -0
  548. package/runtime/python/okstra_vendor/networkx/generators/tests/test_spectral_graph_forge.py +49 -0
  549. package/runtime/python/okstra_vendor/networkx/generators/tests/test_stochastic.py +72 -0
  550. package/runtime/python/okstra_vendor/networkx/generators/tests/test_sudoku.py +92 -0
  551. package/runtime/python/okstra_vendor/networkx/generators/tests/test_time_series.py +64 -0
  552. package/runtime/python/okstra_vendor/networkx/generators/tests/test_trees.py +195 -0
  553. package/runtime/python/okstra_vendor/networkx/generators/tests/test_triads.py +15 -0
  554. package/runtime/python/okstra_vendor/networkx/generators/time_series.py +74 -0
  555. package/runtime/python/okstra_vendor/networkx/generators/trees.py +1070 -0
  556. package/runtime/python/okstra_vendor/networkx/generators/triads.py +94 -0
  557. package/runtime/python/okstra_vendor/networkx/lazy_imports.py +188 -0
  558. package/runtime/python/okstra_vendor/networkx/linalg/__init__.py +13 -0
  559. package/runtime/python/okstra_vendor/networkx/linalg/algebraicconnectivity.py +650 -0
  560. package/runtime/python/okstra_vendor/networkx/linalg/attrmatrix.py +466 -0
  561. package/runtime/python/okstra_vendor/networkx/linalg/bethehessianmatrix.py +77 -0
  562. package/runtime/python/okstra_vendor/networkx/linalg/graphmatrix.py +168 -0
  563. package/runtime/python/okstra_vendor/networkx/linalg/laplacianmatrix.py +512 -0
  564. package/runtime/python/okstra_vendor/networkx/linalg/modularitymatrix.py +166 -0
  565. package/runtime/python/okstra_vendor/networkx/linalg/spectrum.py +186 -0
  566. package/runtime/python/okstra_vendor/networkx/linalg/tests/__init__.py +0 -0
  567. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_algebraic_connectivity.py +400 -0
  568. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_attrmatrix.py +108 -0
  569. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_bethehessian.py +40 -0
  570. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_graphmatrix.py +275 -0
  571. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_laplacian.py +334 -0
  572. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_modularity.py +86 -0
  573. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_spectrum.py +70 -0
  574. package/runtime/python/okstra_vendor/networkx/readwrite/__init__.py +17 -0
  575. package/runtime/python/okstra_vendor/networkx/readwrite/adjlist.py +330 -0
  576. package/runtime/python/okstra_vendor/networkx/readwrite/edgelist.py +489 -0
  577. package/runtime/python/okstra_vendor/networkx/readwrite/gexf.py +1084 -0
  578. package/runtime/python/okstra_vendor/networkx/readwrite/gml.py +879 -0
  579. package/runtime/python/okstra_vendor/networkx/readwrite/graph6.py +427 -0
  580. package/runtime/python/okstra_vendor/networkx/readwrite/graphml.py +1053 -0
  581. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/__init__.py +19 -0
  582. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/adjacency.py +156 -0
  583. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/cytoscape.py +190 -0
  584. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/node_link.py +261 -0
  585. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/__init__.py +0 -0
  586. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_adjacency.py +78 -0
  587. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_cytoscape.py +78 -0
  588. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_node_link.py +109 -0
  589. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_tree.py +48 -0
  590. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tree.py +137 -0
  591. package/runtime/python/okstra_vendor/networkx/readwrite/leda.py +108 -0
  592. package/runtime/python/okstra_vendor/networkx/readwrite/multiline_adjlist.py +393 -0
  593. package/runtime/python/okstra_vendor/networkx/readwrite/p2g.py +113 -0
  594. package/runtime/python/okstra_vendor/networkx/readwrite/pajek.py +286 -0
  595. package/runtime/python/okstra_vendor/networkx/readwrite/sparse6.py +379 -0
  596. package/runtime/python/okstra_vendor/networkx/readwrite/tests/__init__.py +0 -0
  597. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_adjlist.py +354 -0
  598. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_edgelist.py +318 -0
  599. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gexf.py +612 -0
  600. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gml.py +744 -0
  601. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graph6.py +181 -0
  602. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graphml.py +1531 -0
  603. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_leda.py +30 -0
  604. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_p2g.py +63 -0
  605. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_pajek.py +128 -0
  606. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_sparse6.py +166 -0
  607. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_text.py +1742 -0
  608. package/runtime/python/okstra_vendor/networkx/readwrite/text.py +851 -0
  609. package/runtime/python/okstra_vendor/networkx/relabel.py +285 -0
  610. package/runtime/python/okstra_vendor/networkx/tests/__init__.py +0 -0
  611. package/runtime/python/okstra_vendor/networkx/tests/test_all_random_functions.py +248 -0
  612. package/runtime/python/okstra_vendor/networkx/tests/test_convert.py +321 -0
  613. package/runtime/python/okstra_vendor/networkx/tests/test_convert_numpy.py +531 -0
  614. package/runtime/python/okstra_vendor/networkx/tests/test_convert_pandas.py +349 -0
  615. package/runtime/python/okstra_vendor/networkx/tests/test_convert_scipy.py +281 -0
  616. package/runtime/python/okstra_vendor/networkx/tests/test_exceptions.py +40 -0
  617. package/runtime/python/okstra_vendor/networkx/tests/test_import.py +11 -0
  618. package/runtime/python/okstra_vendor/networkx/tests/test_lazy_imports.py +96 -0
  619. package/runtime/python/okstra_vendor/networkx/tests/test_relabel.py +349 -0
  620. package/runtime/python/okstra_vendor/networkx/tests/test_removed_functions_exception_messages.py +8 -0
  621. package/runtime/python/okstra_vendor/networkx/utils/__init__.py +8 -0
  622. package/runtime/python/okstra_vendor/networkx/utils/backends.py +2171 -0
  623. package/runtime/python/okstra_vendor/networkx/utils/configs.py +396 -0
  624. package/runtime/python/okstra_vendor/networkx/utils/decorators.py +1233 -0
  625. package/runtime/python/okstra_vendor/networkx/utils/heaps.py +338 -0
  626. package/runtime/python/okstra_vendor/networkx/utils/mapped_queue.py +297 -0
  627. package/runtime/python/okstra_vendor/networkx/utils/misc.py +703 -0
  628. package/runtime/python/okstra_vendor/networkx/utils/random_sequence.py +198 -0
  629. package/runtime/python/okstra_vendor/networkx/utils/rcm.py +159 -0
  630. package/runtime/python/okstra_vendor/networkx/utils/tests/__init__.py +0 -0
  631. package/runtime/python/okstra_vendor/networkx/utils/tests/test__init.py +11 -0
  632. package/runtime/python/okstra_vendor/networkx/utils/tests/test_backends.py +225 -0
  633. package/runtime/python/okstra_vendor/networkx/utils/tests/test_config.py +263 -0
  634. package/runtime/python/okstra_vendor/networkx/utils/tests/test_decorators.py +510 -0
  635. package/runtime/python/okstra_vendor/networkx/utils/tests/test_heaps.py +131 -0
  636. package/runtime/python/okstra_vendor/networkx/utils/tests/test_mapped_queue.py +268 -0
  637. package/runtime/python/okstra_vendor/networkx/utils/tests/test_misc.py +393 -0
  638. package/runtime/python/okstra_vendor/networkx/utils/tests/test_random_sequence.py +53 -0
  639. package/runtime/python/okstra_vendor/networkx/utils/tests/test_rcm.py +63 -0
  640. package/runtime/python/okstra_vendor/networkx/utils/tests/test_unionfind.py +55 -0
  641. package/runtime/python/okstra_vendor/networkx/utils/union_find.py +106 -0
  642. package/runtime/skills/okstra-graphify/SKILL.md +161 -0
  643. package/runtime/skills/okstra-inspect/SKILL.md +17 -9
  644. package/runtime/templates/reports/settings.template.json +4 -0
  645. package/src/cli-registry.mjs +7 -0
  646. package/src/commands/graphify.mjs +32 -0
  647. package/src/commands/lifecycle/doctor.mjs +9 -0
  648. package/src/lib/skill-catalog.mjs +1 -0
@@ -0,0 +1,2978 @@
1
+ """
2
+ **********
3
+ Matplotlib
4
+ **********
5
+
6
+ Draw networks with matplotlib.
7
+
8
+ Examples
9
+ --------
10
+ >>> G = nx.complete_graph(5)
11
+ >>> nx.draw(G)
12
+
13
+ See Also
14
+ --------
15
+ - :doc:`matplotlib <matplotlib:index>`
16
+ - :func:`matplotlib.pyplot.scatter`
17
+ - :obj:`matplotlib.patches.FancyArrowPatch`
18
+ """
19
+
20
+ import collections
21
+ import itertools
22
+ import math
23
+ from numbers import Number
24
+
25
+ import networkx as nx
26
+
27
+ __all__ = [
28
+ "display",
29
+ "apply_matplotlib_colors",
30
+ "draw",
31
+ "draw_networkx",
32
+ "draw_networkx_nodes",
33
+ "draw_networkx_edges",
34
+ "draw_networkx_labels",
35
+ "draw_networkx_edge_labels",
36
+ "draw_bipartite",
37
+ "draw_circular",
38
+ "draw_kamada_kawai",
39
+ "draw_random",
40
+ "draw_spectral",
41
+ "draw_spring",
42
+ "draw_planar",
43
+ "draw_shell",
44
+ "draw_forceatlas2",
45
+ ]
46
+
47
+
48
+ def apply_matplotlib_colors(
49
+ G, src_attr, dest_attr, map, vmin=None, vmax=None, nodes=True
50
+ ):
51
+ """
52
+ Apply colors from a matplotlib colormap to a graph.
53
+
54
+ Reads values from the `src_attr` and use a matplotlib colormap
55
+ to produce a color. Write the color to `dest_attr`.
56
+
57
+ Parameters
58
+ ----------
59
+ G : nx.Graph
60
+ The graph to read and compute colors for.
61
+
62
+ src_attr : str or other attribute name
63
+ The name of the attribute to read from the graph.
64
+
65
+ dest_attr : str or other attribute name
66
+ The name of the attribute to write to on the graph.
67
+
68
+ map : matplotlib.colormap
69
+ The matplotlib colormap to use.
70
+
71
+ vmin : float, default None
72
+ The minimum value for scaling the colormap. If `None`, find the
73
+ minimum value of `src_attr`.
74
+
75
+ vmax : float, default None
76
+ The maximum value for scaling the colormap. If `None`, find the
77
+ maximum value of `src_attr`.
78
+
79
+ nodes : bool, default True
80
+ Whether the attribute names are edge attributes or node attributes.
81
+ """
82
+ import matplotlib as mpl
83
+
84
+ if nodes:
85
+ type_iter = G.nodes()
86
+ elif G.is_multigraph():
87
+ type_iter = G.edges(keys=True)
88
+ else:
89
+ type_iter = G.edges()
90
+
91
+ if vmin is None or vmax is None:
92
+ vals = [type_iter[a][src_attr] for a in type_iter]
93
+ if vmin is None:
94
+ vmin = min(vals)
95
+ if vmax is None:
96
+ vmax = max(vals)
97
+
98
+ mapper = mpl.cm.ScalarMappable(cmap=map)
99
+ mapper.set_clim(vmin, vmax)
100
+
101
+ def do_map(x):
102
+ # Cast numpy scalars to float
103
+ return tuple(float(x) for x in mapper.to_rgba(x))
104
+
105
+ if nodes:
106
+ nx.set_node_attributes(
107
+ G, {n: do_map(G.nodes[n][src_attr]) for n in G.nodes()}, dest_attr
108
+ )
109
+ else:
110
+ nx.set_edge_attributes(
111
+ G, {e: do_map(G.edges[e][src_attr]) for e in type_iter}, dest_attr
112
+ )
113
+
114
+
115
+ class CurvedArrowTextBase:
116
+ def __init__(
117
+ self,
118
+ arrow,
119
+ *args,
120
+ label_pos=0.5,
121
+ labels_horizontal=False,
122
+ ax=None,
123
+ **kwargs,
124
+ ):
125
+ # Bind to FancyArrowPatch
126
+ self.arrow = arrow
127
+ # how far along the text should be on the curve,
128
+ # 0 is at start, 1 is at end etc.
129
+ self.label_pos = label_pos
130
+ self.labels_horizontal = labels_horizontal
131
+ if ax is None:
132
+ ax = plt.gca()
133
+ self.ax = ax
134
+ self.x, self.y, self.angle = self._update_text_pos_angle(arrow)
135
+
136
+ # Create text object
137
+ super().__init__(self.x, self.y, *args, rotation=self.angle, **kwargs)
138
+ # Bind to axis
139
+ self.ax.add_artist(self)
140
+
141
+ def _get_arrow_path_disp(self, arrow):
142
+ """
143
+ This is part of FancyArrowPatch._get_path_in_displaycoord
144
+ It omits the second part of the method where path is converted
145
+ to polygon based on width
146
+ The transform is taken from ax, not the object, as the object
147
+ has not been added yet, and doesn't have transform
148
+ """
149
+ dpi_cor = arrow._dpi_cor
150
+ trans_data = self.ax.transData
151
+ if arrow._posA_posB is None:
152
+ raise ValueError(
153
+ "Can only draw labels for fancy arrows with "
154
+ "posA and posB inputs, not custom path"
155
+ )
156
+ posA = arrow._convert_xy_units(arrow._posA_posB[0])
157
+ posB = arrow._convert_xy_units(arrow._posA_posB[1])
158
+ (posA, posB) = trans_data.transform((posA, posB))
159
+ _path = arrow.get_connectionstyle()(
160
+ posA,
161
+ posB,
162
+ patchA=arrow.patchA,
163
+ patchB=arrow.patchB,
164
+ shrinkA=arrow.shrinkA * dpi_cor,
165
+ shrinkB=arrow.shrinkB * dpi_cor,
166
+ )
167
+ # Return is in display coordinates
168
+ return _path
169
+
170
+ def _update_text_pos_angle(self, arrow):
171
+ # Fractional label position
172
+ # Text position at a proportion t along the line in display coords
173
+ # default is 0.5 so text appears at the halfway point
174
+ import matplotlib as mpl
175
+ import numpy as np
176
+
177
+ t = self.label_pos
178
+ tt = 1 - t
179
+ path_disp = self._get_arrow_path_disp(arrow)
180
+ conn = arrow.get_connectionstyle()
181
+ # 1. Calculate x and y
182
+ points = path_disp.vertices
183
+ if is_curve := isinstance(
184
+ conn,
185
+ mpl.patches.ConnectionStyle.Angle3 | mpl.patches.ConnectionStyle.Arc3,
186
+ ):
187
+ # Arc3 or Angle3 type Connection Styles - Bezier curve
188
+ (x1, y1), (cx, cy), (x2, y2) = points
189
+ x = tt**2 * x1 + 2 * t * tt * cx + t**2 * x2
190
+ y = tt**2 * y1 + 2 * t * tt * cy + t**2 * y2
191
+ else:
192
+ if not isinstance(
193
+ conn,
194
+ mpl.patches.ConnectionStyle.Angle
195
+ | mpl.patches.ConnectionStyle.Arc
196
+ | mpl.patches.ConnectionStyle.Bar,
197
+ ):
198
+ msg = f"invalid connection style: {type(conn)}"
199
+ raise TypeError(msg)
200
+ # A. Collect lines
201
+ codes = path_disp.codes
202
+ lines = [
203
+ points[i - 1 : i + 1]
204
+ for i in range(1, len(points))
205
+ if codes[i] == mpl.path.Path.LINETO
206
+ ]
207
+ # B. If more than one line, find the right one and position in it
208
+ if (nlines := len(lines)) != 1:
209
+ dists = [math.dist(*line) for line in lines]
210
+ dist_tot = sum(dists)
211
+ cdist = 0
212
+ last_cut = 0
213
+ i_last = nlines - 1
214
+ for i, dist in enumerate(dists):
215
+ cdist += dist
216
+ cut = cdist / dist_tot
217
+ if i == i_last or t < cut:
218
+ t = (t - last_cut) / (dist / dist_tot)
219
+ tt = 1 - t
220
+ lines = [lines[i]]
221
+ break
222
+ last_cut = cut
223
+ [[(cx1, cy1), (cx2, cy2)]] = lines
224
+ x = cx1 * tt + cx2 * t
225
+ y = cy1 * tt + cy2 * t
226
+
227
+ # 2. Calculate Angle
228
+ if self.labels_horizontal:
229
+ # Horizontal text labels
230
+ angle = 0
231
+ else:
232
+ # Labels parallel to curve
233
+ if is_curve:
234
+ change_x = 2 * tt * (cx - x1) + 2 * t * (x2 - cx)
235
+ change_y = 2 * tt * (cy - y1) + 2 * t * (y2 - cy)
236
+ else:
237
+ change_x = (cx2 - cx1) / 2
238
+ change_y = (cy2 - cy1) / 2
239
+ angle = np.arctan2(change_y, change_x) / (2 * np.pi) * 360
240
+ # Text is "right way up"
241
+ if angle > 90:
242
+ angle -= 180
243
+ elif angle < -90:
244
+ angle += 180
245
+ (x, y) = self.ax.transData.inverted().transform((x, y))
246
+ return x, y, angle
247
+
248
+ def draw(self, renderer):
249
+ # recalculate the text position and angle
250
+ self.x, self.y, self.angle = self._update_text_pos_angle(self.arrow)
251
+ self.set_position((self.x, self.y))
252
+ self.set_rotation(self.angle)
253
+ # redraw text
254
+ super().draw(renderer)
255
+
256
+
257
+ def display(
258
+ G,
259
+ canvas=None,
260
+ **kwargs,
261
+ ):
262
+ """Draw the graph G.
263
+
264
+ Draw the graph as a collection of nodes connected by edges.
265
+ The exact details of what the graph looks like are controlled by the below
266
+ attributes. All nodes and nodes at the end of visible edges must have a
267
+ position set, but nearly all other node and edge attributes are options and
268
+ nodes or edges missing the attribute will use the default listed below. A more
269
+ complete description of each parameter is given below this summary.
270
+
271
+ .. list-table:: Default Visualization Attributes
272
+ :widths: 25 25 50
273
+ :header-rows: 1
274
+
275
+ * - Parameter
276
+ - Default Attribute
277
+ - Default Value
278
+ * - node_pos
279
+ - `"pos"`
280
+ - If there is not position, a layout will be calculated with `nx.spring_layout`.
281
+ * - node_visible
282
+ - `"visible"`
283
+ - True
284
+ * - node_color
285
+ - `"color"`
286
+ - #1f78b4
287
+ * - node_size
288
+ - `"size"`
289
+ - 300
290
+ * - node_label
291
+ - `"label"`
292
+ - Dict describing the node label. Defaults create a black text with
293
+ the node name as the label. The dict respects these keys and defaults:
294
+
295
+ * size : 12
296
+ * color : black
297
+ * family : sans serif
298
+ * weight : normal
299
+ * alpha : 1.0
300
+ * h_align : center
301
+ * v_align : center
302
+ * bbox : Dict describing a `matplotlib.patches.FancyBboxPatch`.
303
+ Default is None.
304
+
305
+ * - node_shape
306
+ - `"shape"`
307
+ - "o"
308
+ * - node_alpha
309
+ - `"alpha"`
310
+ - 1.0
311
+ * - node_border_width
312
+ - `"border_width"`
313
+ - 1.0
314
+ * - node_border_color
315
+ - `"border_color"`
316
+ - Matching node_color
317
+ * - edge_visible
318
+ - `"visible"`
319
+ - True
320
+ * - edge_width
321
+ - `"width"`
322
+ - 1.0
323
+ * - edge_color
324
+ - `"color"`
325
+ - Black (#000000)
326
+ * - edge_label
327
+ - `"label"`
328
+ - Dict describing the edge label. Defaults create black text with a
329
+ white bounding box. The dictionary respects these keys and defaults:
330
+
331
+ * size : 12
332
+ * color : black
333
+ * family : sans serif
334
+ * weight : normal
335
+ * alpha : 1.0
336
+ * bbox : Dict describing a `matplotlib.patches.FancyBboxPatch`.
337
+ Default {"boxstyle": "round", "ec": (1.0, 1.0, 1.0), "fc": (1.0, 1.0, 1.0)}
338
+ * h_align : "center"
339
+ * v_align : "center"
340
+ * pos : 0.5
341
+ * rotate : True
342
+
343
+ * - edge_style
344
+ - `"style"`
345
+ - "-"
346
+ * - edge_alpha
347
+ - `"alpha"`
348
+ - 1.0
349
+ * - edge_arrowstyle
350
+ - `"arrowstyle"`
351
+ - ``"-|>"`` if `G` is directed else ``"-"``
352
+ * - edge_arrowsize
353
+ - `"arrowsize"`
354
+ - 10 if `G` is directed else 0
355
+ * - edge_curvature
356
+ - `"curvature"`
357
+ - arc3
358
+ * - edge_source_margin
359
+ - `"source_margin"`
360
+ - 0
361
+ * - edge_target_margin
362
+ - `"target_margin"`
363
+ - 0
364
+
365
+ Parameters
366
+ ----------
367
+ G : graph
368
+ A networkx graph
369
+
370
+ canvas : Matplotlib Axes object, optional
371
+ Draw the graph in specified Matplotlib axes
372
+
373
+ node_pos : string or function, default "pos"
374
+ A string naming the node attribute storing the position of nodes as a tuple.
375
+ Or a function to be called with input `G` which returns the layout as a dict keyed
376
+ by node to position tuple like the NetworkX layout functions.
377
+ If no nodes in the graph has the attribute, a spring layout is calculated.
378
+
379
+ node_visible : string or bool, default visible
380
+ A string naming the node attribute which stores if a node should be drawn.
381
+ If `True`, all nodes will be visible while if `False` no nodes will be visible.
382
+ If incomplete, nodes missing this attribute will be shown by default.
383
+
384
+ node_color : string, default "color"
385
+ A string naming the node attribute which stores the color of each node.
386
+ Visible nodes without this attribute will use '#1f78b4' as a default.
387
+
388
+ node_size : string or number, default "size"
389
+ A string naming the node attribute which stores the size of each node.
390
+ Visible nodes without this attribute will use a default size of 300.
391
+
392
+ node_label : string or bool, default "label"
393
+ A string naming the node attribute which stores the label of each node.
394
+ The attribute value can be a string, False (no label for that node),
395
+ True (the node is the label) or a dict keyed by node to the label.
396
+
397
+ If a dict is specified, these keys are read to further control the label:
398
+
399
+ * label : The text of the label; default: name of the node
400
+ * size : Font size of the label; default: 12
401
+ * color : Font color of the label; default: black
402
+ * family : Font family of the label; default: "sans-serif"
403
+ * weight : Font weight of the label; default: "normal"
404
+ * alpha : Alpha value of the label; default: 1.0
405
+ * h_align : The horizontal alignment of the label.
406
+ one of "left", "center", "right"; default: "center"
407
+ * v_align : The vertical alignment of the label.
408
+ one of "top", "center", "bottom"; default: "center"
409
+ * bbox : A dict of parameters for `matplotlib.patches.FancyBboxPatch`.
410
+
411
+ Visible nodes without this attribute will be treated as if the value was True.
412
+
413
+ node_shape : string, default "shape"
414
+ A string naming the node attribute which stores the label of each node.
415
+ The values of this attribute are expected to be one of the matplotlib shapes,
416
+ one of 'so^>v<dph8'. Visible nodes without this attribute will use 'o'.
417
+
418
+ node_alpha : string, default "alpha"
419
+ A string naming the node attribute which stores the alpha of each node.
420
+ The values of this attribute are expected to be floats between 0.0 and 1.0.
421
+ Visible nodes without this attribute will be treated as if the value was 1.0.
422
+
423
+ node_border_width : string, default "border_width"
424
+ A string naming the node attribute storing the width of the border of the node.
425
+ The values of this attribute are expected to be numeric. Visible nodes without
426
+ this attribute will use the assumed default of 1.0.
427
+
428
+ node_border_color : string, default "border_color"
429
+ A string naming the node attribute which storing the color of the border of the node.
430
+ Visible nodes missing this attribute will use the final node_color value.
431
+
432
+ edge_visible : string or bool, default "visible"
433
+ A string nameing the edge attribute which stores if an edge should be drawn.
434
+ If `True`, all edges will be drawn while if `False` no edges will be visible.
435
+ If incomplete, edges missing this attribute will be shown by default. Values
436
+ of this attribute are expected to be booleans.
437
+
438
+ edge_width : string or int, default "width"
439
+ A string nameing the edge attribute which stores the width of each edge.
440
+ Visible edges without this attribute will use a default width of 1.0.
441
+
442
+ edge_color : string or color, default "color"
443
+ A string nameing the edge attribute which stores of color of each edge.
444
+ Visible edges without this attribute will be drawn black. Each color can be
445
+ a string or rgb (or rgba) tuple of floats from 0.0 to 1.0.
446
+
447
+ edge_label : string, default "label"
448
+ A string naming the edge attribute which stores the label of each edge.
449
+ The values of this attribute can be a string, number or False or None. In
450
+ the latter two cases, no edge label is displayed.
451
+
452
+ If a dict is specified, these keys are read to further control the label:
453
+
454
+ * label : The text of the label, or the name of an edge attribute holding the label.
455
+ * size : Font size of the label; default: 12
456
+ * color : Font color of the label; default: black
457
+ * family : Font family of the label; default: "sans-serif"
458
+ * weight : Font weight of the label; default: "normal"
459
+ * alpha : Alpha value of the label; default: 1.0
460
+ * h_align : The horizontal alignment of the label.
461
+ one of "left", "center", "right"; default: "center"
462
+ * v_align : The vertical alignment of the label.
463
+ one of "top", "center", "bottom"; default: "center"
464
+ * bbox : A dict of parameters for `matplotlib.patches.FancyBboxPatch`.
465
+ * rotate : Whether to rotate labels to lie parallel to the edge, default: True.
466
+ * pos : A float showing how far along the edge to put the label; default: 0.5.
467
+
468
+ edge_style : string, default "style"
469
+ A string naming the edge attribute which stores the style of each edge.
470
+ Visible edges without this attribute will be drawn solid. Values of this
471
+ attribute can be line styles, e.g. '-', '--', '-.' or ':' or words like 'solid'
472
+ or 'dashed'. If no edge in the graph has this attribute and it is a non-default
473
+ value, assume that it describes the edge style for all edges in the graph.
474
+
475
+ edge_alpha : string or float, default "alpha"
476
+ A string naming the edge attribute which stores the alpha value of each edge.
477
+ Visible edges without this attribute will use an alpha value of 1.0.
478
+
479
+ edge_arrowstyle : string, default "arrowstyle"
480
+ A string naming the edge attribute which stores the type of arrowhead to use for
481
+ each edge. Visible edges without this attribute use ``"-"`` for undirected graphs
482
+ and ``"-|>"`` for directed graphs.
483
+
484
+ See `matplotlib.patches.ArrowStyle` for more options
485
+
486
+ edge_arrowsize : string or int, default "arrowsize"
487
+ A string naming the edge attribute which stores the size of the arrowhead for each
488
+ edge. Visible edges without this attribute will use a default value of 10.
489
+
490
+ edge_curvature : string, default "curvature"
491
+ A string naming the edge attribute storing the curvature and connection style
492
+ of each edge. Visible edges without this attribute will use "arc3" as a default
493
+ value, resulting an a straight line between the two nodes. Curvature can be given
494
+ as 'arc3,rad=0.2' to specify both the style and radius of curvature.
495
+
496
+ Please see `matplotlib.patches.ConnectionStyle` and
497
+ `matplotlib.patches.FancyArrowPatch` for more information.
498
+
499
+ edge_source_margin : string or int, default "source_margin"
500
+ A string naming the edge attribute which stores the minimum margin (gap) between
501
+ the source node and the start of the edge. Visible edges without this attribute
502
+ will use a default value of 0.
503
+
504
+ edge_target_margin : string or int, default "target_margin"
505
+ A string naming the edge attribute which stores the minimumm margin (gap) between
506
+ the target node and the end of the edge. Visible edges without this attribute
507
+ will use a default value of 0.
508
+
509
+ hide_ticks : bool, default True
510
+ Weather to remove the ticks from the axes of the matplotlib object.
511
+
512
+ Raises
513
+ ------
514
+ NetworkXError
515
+ If a node or edge is missing a required parameter such as `pos` or
516
+ if `display` receives an argument not listed above.
517
+
518
+ ValueError
519
+ If a node or edge has an invalid color format, i.e. not a color string,
520
+ rgb tuple or rgba tuple.
521
+
522
+ Returns
523
+ -------
524
+ The input graph. This is potentially useful for dispatching visualization
525
+ functions.
526
+ """
527
+ from collections import Counter
528
+
529
+ import matplotlib as mpl
530
+ import matplotlib.pyplot as plt
531
+ import numpy as np
532
+
533
+ defaults = {
534
+ "node_pos": None,
535
+ "node_visible": True,
536
+ "node_color": "#1f78b4",
537
+ "node_size": 300,
538
+ "node_label": {
539
+ "size": 12,
540
+ "color": "#000000",
541
+ "family": "sans-serif",
542
+ "weight": "normal",
543
+ "alpha": 1.0,
544
+ "h_align": "center",
545
+ "v_align": "center",
546
+ "bbox": None,
547
+ },
548
+ "node_shape": "o",
549
+ "node_alpha": 1.0,
550
+ "node_border_width": 1.0,
551
+ "node_border_color": "face",
552
+ "edge_visible": True,
553
+ "edge_width": 1.0,
554
+ "edge_color": "#000000",
555
+ "edge_label": {
556
+ "size": 12,
557
+ "color": "#000000",
558
+ "family": "sans-serif",
559
+ "weight": "normal",
560
+ "alpha": 1.0,
561
+ "bbox": {"boxstyle": "round", "ec": (1.0, 1.0, 1.0), "fc": (1.0, 1.0, 1.0)},
562
+ "h_align": "center",
563
+ "v_align": "center",
564
+ "pos": 0.5,
565
+ "rotate": True,
566
+ },
567
+ "edge_style": "-",
568
+ "edge_alpha": 1.0,
569
+ "edge_arrowstyle": "-|>" if G.is_directed() else "-",
570
+ "edge_arrowsize": 10 if G.is_directed() else 0,
571
+ "edge_curvature": "arc3",
572
+ "edge_source_margin": 0,
573
+ "edge_target_margin": 0,
574
+ "hide_ticks": True,
575
+ }
576
+
577
+ # Check arguments
578
+ for kwarg in kwargs:
579
+ if kwarg not in defaults:
580
+ raise nx.NetworkXError(
581
+ f"Unrecognized visualization keyword argument: {kwarg}"
582
+ )
583
+
584
+ if canvas is None:
585
+ canvas = plt.gca()
586
+
587
+ if kwargs.get("hide_ticks", defaults["hide_ticks"]):
588
+ canvas.tick_params(
589
+ axis="both",
590
+ which="both",
591
+ bottom=False,
592
+ left=False,
593
+ labelbottom=False,
594
+ labelleft=False,
595
+ )
596
+
597
+ ### Helper methods and classes
598
+
599
+ def node_property_sequence(seq, attr):
600
+ """Return a list of attribute values for `seq`, using a default if needed"""
601
+
602
+ # All node attribute parameters start with "node_"
603
+ param_name = f"node_{attr}"
604
+ default = defaults[param_name]
605
+ attr = kwargs.get(param_name, attr)
606
+
607
+ if default is None:
608
+ # raise instead of using non-existant default value
609
+ for n in seq:
610
+ if attr not in node_subgraph.nodes[n]:
611
+ raise nx.NetworkXError(f"Attribute '{attr}' missing for node {n}")
612
+
613
+ # If `attr` is not a graph attr and was explicitly passed as an argument
614
+ # it must be a user-default value. Allow attr=None to tell draw to skip
615
+ # attributes which are on the graph
616
+ if (
617
+ attr is not None
618
+ and nx.get_node_attributes(node_subgraph, attr) == {}
619
+ and any(attr == v for k, v in kwargs.items() if "node" in k)
620
+ ):
621
+ return [attr for _ in seq]
622
+
623
+ return [node_subgraph.nodes[n].get(attr, default) for n in seq]
624
+
625
+ def compute_colors(color, alpha):
626
+ if isinstance(color, str):
627
+ rgba = mpl.colors.colorConverter.to_rgba(color)
628
+ # Using a non-default alpha value overrides any alpha value in the color
629
+ if alpha != defaults["node_alpha"]:
630
+ return (rgba[0], rgba[1], rgba[2], alpha)
631
+ return rgba
632
+
633
+ if isinstance(color, tuple) and len(color) == 3:
634
+ return (color[0], color[1], color[2], alpha)
635
+
636
+ if isinstance(color, tuple) and len(color) == 4:
637
+ return color
638
+
639
+ raise ValueError(f"Invalid format for color: {color}")
640
+
641
+ # Find which edges can be plotted as a line collection
642
+ #
643
+ # Non-default values for these attributes require fancy arrow patches:
644
+ # - any arrow style (including the default -|> for directed graphs)
645
+ # - arrow size (by extension of style)
646
+ # - connection style
647
+ # - min_source_margin
648
+ # - min_target_margin
649
+
650
+ def collection_compatible(e):
651
+ return (
652
+ get_edge_attr(e, "arrowstyle") == "-"
653
+ and get_edge_attr(e, "curvature") == "arc3"
654
+ and get_edge_attr(e, "source_margin") == 0
655
+ and get_edge_attr(e, "target_margin") == 0
656
+ # Self-loops will use fancy arrow patches
657
+ and e[0] != e[1]
658
+ )
659
+
660
+ def edge_property_sequence(seq, attr):
661
+ """Return a list of attribute values for `seq`, using a default if needed"""
662
+
663
+ param_name = f"edge_{attr}"
664
+ default = defaults[param_name]
665
+ attr = kwargs.get(param_name, attr)
666
+
667
+ if default is None:
668
+ # raise instead of using non-existant default value
669
+ for e in seq:
670
+ if attr not in edge_subgraph.edges[e]:
671
+ raise nx.NetworkXError(f"Attribute '{attr}' missing for edge {e}")
672
+
673
+ if (
674
+ attr is not None
675
+ and nx.get_edge_attributes(edge_subgraph, attr) == {}
676
+ and any(attr == v for k, v in kwargs.items() if "edge" in k)
677
+ ):
678
+ return [attr for _ in seq]
679
+
680
+ return [edge_subgraph.edges[e].get(attr, default) for e in seq]
681
+
682
+ def get_edge_attr(e, attr):
683
+ """Return the final edge attribute value, using default if not None"""
684
+
685
+ param_name = f"edge_{attr}"
686
+ default = defaults[param_name]
687
+ attr = kwargs.get(param_name, attr)
688
+
689
+ if default is None and attr not in edge_subgraph.edges[e]:
690
+ raise nx.NetworkXError(f"Attribute '{attr}' missing from edge {e}")
691
+
692
+ if (
693
+ attr is not None
694
+ and nx.get_edge_attributes(edge_subgraph, attr) == {}
695
+ and attr in kwargs.values()
696
+ ):
697
+ return attr
698
+
699
+ return edge_subgraph.edges[e].get(attr, default)
700
+
701
+ def get_node_attr(n, attr, use_edge_subgraph=True):
702
+ """Return the final node attribute value, using default if not None"""
703
+ subgraph = edge_subgraph if use_edge_subgraph else node_subgraph
704
+
705
+ param_name = f"node_{attr}"
706
+ default = defaults[param_name]
707
+ attr = kwargs.get(param_name, attr)
708
+
709
+ if default is None and attr not in subgraph.nodes[n]:
710
+ raise nx.NetworkXError(f"Attribute '{attr}' missing from node {n}")
711
+
712
+ if (
713
+ attr is not None
714
+ and nx.get_node_attributes(subgraph, attr) == {}
715
+ and attr in kwargs.values()
716
+ ):
717
+ return attr
718
+
719
+ return subgraph.nodes[n].get(attr, default)
720
+
721
+ # Taken from ConnectionStyleFactory
722
+ def self_loop(edge_index, node_size):
723
+ def self_loop_connection(posA, posB, *args, **kwargs):
724
+ if not np.all(posA == posB):
725
+ raise nx.NetworkXError(
726
+ "`self_loop` connection style method"
727
+ "is only to be used for self-loops"
728
+ )
729
+ # this is called with _screen space_ values
730
+ # so convert back to data space
731
+ data_loc = canvas.transData.inverted().transform(posA)
732
+ # Scale self loop based on the size of the base node
733
+ # Size of nodes are given in points ** 2 and each point is 1/72 of an inch
734
+ v_shift = np.sqrt(node_size) / 72
735
+ h_shift = v_shift * 0.5
736
+ # put the top of the loop first so arrow is not hidden by node
737
+ path = np.asarray(
738
+ [
739
+ # 1
740
+ [0, v_shift],
741
+ # 4 4 4
742
+ [h_shift, v_shift],
743
+ [h_shift, 0],
744
+ [0, 0],
745
+ # 4 4 4
746
+ [-h_shift, 0],
747
+ [-h_shift, v_shift],
748
+ [0, v_shift],
749
+ ]
750
+ )
751
+ # Rotate self loop 90 deg. if more than 1
752
+ # This will allow for maximum of 4 visible self loops
753
+ if edge_index % 4:
754
+ x, y = path.T
755
+ for _ in range(edge_index % 4):
756
+ x, y = y, -x
757
+ path = np.array([x, y]).T
758
+ return mpl.path.Path(
759
+ canvas.transData.transform(data_loc + path), [1, 4, 4, 4, 4, 4, 4]
760
+ )
761
+
762
+ return self_loop_connection
763
+
764
+ def to_marker_edge(size, marker):
765
+ if marker in "s^>v<d":
766
+ return np.sqrt(2 * size) / 2
767
+ else:
768
+ return np.sqrt(size) / 2
769
+
770
+ def build_fancy_arrow(e):
771
+ source_margin = to_marker_edge(
772
+ get_node_attr(e[0], "size"),
773
+ get_node_attr(e[0], "shape"),
774
+ )
775
+ source_margin = max(
776
+ source_margin,
777
+ get_edge_attr(e, "source_margin"),
778
+ )
779
+
780
+ target_margin = to_marker_edge(
781
+ get_node_attr(e[1], "size"),
782
+ get_node_attr(e[1], "shape"),
783
+ )
784
+ target_margin = max(
785
+ target_margin,
786
+ get_edge_attr(e, "target_margin"),
787
+ )
788
+ return mpl.patches.FancyArrowPatch(
789
+ edge_subgraph.nodes[e[0]][pos],
790
+ edge_subgraph.nodes[e[1]][pos],
791
+ arrowstyle=get_edge_attr(e, "arrowstyle"),
792
+ connectionstyle=(
793
+ get_edge_attr(e, "curvature")
794
+ if e[0] != e[1]
795
+ else self_loop(
796
+ 0 if len(e) == 2 else e[2] % 4,
797
+ get_node_attr(e[0], "size"),
798
+ )
799
+ ),
800
+ color=get_edge_attr(e, "color"),
801
+ linestyle=get_edge_attr(e, "style"),
802
+ linewidth=get_edge_attr(e, "width"),
803
+ mutation_scale=get_edge_attr(e, "arrowsize"),
804
+ shrinkA=source_margin,
805
+ shrinkB=source_margin,
806
+ zorder=1,
807
+ )
808
+
809
+ class CurvedArrowText(CurvedArrowTextBase, mpl.text.Text):
810
+ pass
811
+
812
+ ### Draw the nodes first
813
+ node_visible = kwargs.get("node_visible", "visible")
814
+ if isinstance(node_visible, bool):
815
+ if node_visible:
816
+ visible_nodes = G.nodes()
817
+ else:
818
+ visible_nodes = []
819
+ else:
820
+ visible_nodes = [
821
+ n for n, v in nx.get_node_attributes(G, node_visible, True).items() if v
822
+ ]
823
+
824
+ node_subgraph = G.subgraph(visible_nodes)
825
+
826
+ # Ignore the default dict value since that's for default values to use, not
827
+ # default attribute name
828
+ pos = kwargs.get("node_pos", "pos")
829
+
830
+ default_display_pos_attr = "display's position attribute name"
831
+ if callable(pos):
832
+ nx.set_node_attributes(
833
+ node_subgraph, pos(node_subgraph), default_display_pos_attr
834
+ )
835
+ pos = default_display_pos_attr
836
+ kwargs["node_pos"] = default_display_pos_attr
837
+ elif nx.get_node_attributes(G, pos) == {}:
838
+ nx.set_node_attributes(
839
+ node_subgraph, nx.spring_layout(node_subgraph), default_display_pos_attr
840
+ )
841
+ pos = default_display_pos_attr
842
+ kwargs["node_pos"] = default_display_pos_attr
843
+
844
+ # Each shape requires a new scatter object since they can't have different
845
+ # shapes.
846
+ if len(visible_nodes) > 0:
847
+ node_shape = kwargs.get("node_shape", "shape")
848
+ for shape in Counter(
849
+ nx.get_node_attributes(
850
+ node_subgraph, node_shape, defaults["node_shape"]
851
+ ).values()
852
+ ):
853
+ # Filter position just on this shape.
854
+ nodes_with_shape = [
855
+ n
856
+ for n, s in node_subgraph.nodes(data=node_shape)
857
+ if s == shape or (s is None and shape == defaults["node_shape"])
858
+ ]
859
+ # There are two property sequences to create before hand.
860
+ # 1. position, since it is used for x and y parameters to scatter
861
+ # 2. edgecolor, since the spaeical 'face' parameter value can only be
862
+ # be passed in as the sole string, not part of a list of strings.
863
+ position = np.asarray(node_property_sequence(nodes_with_shape, "pos"))
864
+ color = np.asarray(
865
+ [
866
+ compute_colors(c, a)
867
+ for c, a in zip(
868
+ node_property_sequence(nodes_with_shape, "color"),
869
+ node_property_sequence(nodes_with_shape, "alpha"),
870
+ )
871
+ ]
872
+ )
873
+ border_color = np.asarray(
874
+ [
875
+ (
876
+ c
877
+ if (
878
+ c := get_node_attr(
879
+ n,
880
+ "border_color",
881
+ False,
882
+ )
883
+ )
884
+ != "face"
885
+ else color[i]
886
+ )
887
+ for i, n in enumerate(nodes_with_shape)
888
+ ]
889
+ )
890
+ canvas.scatter(
891
+ position[:, 0],
892
+ position[:, 1],
893
+ s=node_property_sequence(nodes_with_shape, "size"),
894
+ c=color,
895
+ marker=shape,
896
+ linewidths=node_property_sequence(nodes_with_shape, "border_width"),
897
+ edgecolors=border_color,
898
+ zorder=2,
899
+ )
900
+
901
+ ### Draw node labels
902
+ node_label = kwargs.get("node_label", "label")
903
+ # Plot labels if node_label is not None and not False
904
+ if node_label is not None and node_label is not False:
905
+ default_dict = {}
906
+ if isinstance(node_label, dict):
907
+ default_dict = node_label
908
+ node_label = None
909
+
910
+ for n, lbl in node_subgraph.nodes(data=node_label):
911
+ if lbl is False:
912
+ continue
913
+
914
+ # We work with label dicts down here...
915
+ if not isinstance(lbl, dict):
916
+ lbl = {"label": lbl if lbl is not None else n}
917
+
918
+ lbl_text = lbl.get("label", n)
919
+ if not isinstance(lbl_text, str):
920
+ lbl_text = str(lbl_text)
921
+
922
+ lbl.update(default_dict)
923
+ x, y = node_subgraph.nodes[n][pos]
924
+ canvas.text(
925
+ x,
926
+ y,
927
+ lbl_text,
928
+ size=lbl.get("size", defaults["node_label"]["size"]),
929
+ color=lbl.get("color", defaults["node_label"]["color"]),
930
+ family=lbl.get("family", defaults["node_label"]["family"]),
931
+ weight=lbl.get("weight", defaults["node_label"]["weight"]),
932
+ horizontalalignment=lbl.get(
933
+ "h_align", defaults["node_label"]["h_align"]
934
+ ),
935
+ verticalalignment=lbl.get("v_align", defaults["node_label"]["v_align"]),
936
+ transform=canvas.transData,
937
+ bbox=lbl.get("bbox", defaults["node_label"]["bbox"]),
938
+ )
939
+
940
+ ### Draw edges
941
+
942
+ edge_visible = kwargs.get("edge_visible", "visible")
943
+ if isinstance(edge_visible, bool):
944
+ if edge_visible:
945
+ visible_edges = G.edges()
946
+ else:
947
+ visible_edges = []
948
+ else:
949
+ visible_edges = [
950
+ e for e, v in nx.get_edge_attributes(G, edge_visible, True).items() if v
951
+ ]
952
+
953
+ edge_subgraph = G.edge_subgraph(visible_edges)
954
+ nx.set_node_attributes(
955
+ edge_subgraph, nx.get_node_attributes(node_subgraph, pos), name=pos
956
+ )
957
+
958
+ collection_edges = (
959
+ [e for e in edge_subgraph.edges(keys=True) if collection_compatible(e)]
960
+ if edge_subgraph.is_multigraph()
961
+ else [e for e in edge_subgraph.edges() if collection_compatible(e)]
962
+ )
963
+ non_collection_edges = (
964
+ [e for e in edge_subgraph.edges(keys=True) if not collection_compatible(e)]
965
+ if edge_subgraph.is_multigraph()
966
+ else [e for e in edge_subgraph.edges() if not collection_compatible(e)]
967
+ )
968
+ edge_position = np.asarray(
969
+ [
970
+ (
971
+ get_node_attr(u, "pos", use_edge_subgraph=True),
972
+ get_node_attr(v, "pos", use_edge_subgraph=True),
973
+ )
974
+ for u, v, *_ in collection_edges
975
+ ]
976
+ )
977
+
978
+ # Only plot a line collection if needed
979
+ if len(collection_edges) > 0:
980
+ edge_collection = mpl.collections.LineCollection(
981
+ edge_position,
982
+ colors=edge_property_sequence(collection_edges, "color"),
983
+ linewidths=edge_property_sequence(collection_edges, "width"),
984
+ linestyle=edge_property_sequence(collection_edges, "style"),
985
+ alpha=edge_property_sequence(collection_edges, "alpha"),
986
+ antialiaseds=(1,),
987
+ zorder=1,
988
+ )
989
+ canvas.add_collection(edge_collection)
990
+
991
+ fancy_arrows = {}
992
+ if len(non_collection_edges) > 0:
993
+ for e in non_collection_edges:
994
+ # Cache results for use in edge labels
995
+ fancy_arrows[e] = build_fancy_arrow(e)
996
+ canvas.add_patch(fancy_arrows[e])
997
+
998
+ ### Draw edge labels
999
+ edge_label = kwargs.get("edge_label", "label")
1000
+ default_dict = {}
1001
+ if isinstance(edge_label, dict):
1002
+ default_dict = edge_label
1003
+ # Restore the default label attribute key of 'label'
1004
+ edge_label = "label"
1005
+
1006
+ # Handle multigraphs
1007
+ edge_label_data = (
1008
+ edge_subgraph.edges(data=edge_label, keys=True)
1009
+ if edge_subgraph.is_multigraph()
1010
+ else edge_subgraph.edges(data=edge_label)
1011
+ )
1012
+ if edge_label is not None and edge_label is not False:
1013
+ for *e, lbl in edge_label_data:
1014
+ e = tuple(e)
1015
+ # I'm not sure how I want to handle None here... For now it means no label
1016
+ if lbl is False or lbl is None:
1017
+ continue
1018
+
1019
+ if not isinstance(lbl, dict):
1020
+ lbl = {"label": lbl}
1021
+
1022
+ lbl.update(default_dict)
1023
+ lbl_text = lbl.get("label")
1024
+ if not isinstance(lbl_text, str):
1025
+ lbl_text = str(lbl_text)
1026
+
1027
+ # In the old code, every non-self-loop is placed via a fancy arrow patch
1028
+ # Only compute a new fancy arrow if needed by caching the results from
1029
+ # edge placement.
1030
+ try:
1031
+ arrow = fancy_arrows[e]
1032
+ except KeyError:
1033
+ arrow = build_fancy_arrow(e)
1034
+
1035
+ if e[0] == e[1]:
1036
+ # Taken directly from draw_networkx_edge_labels
1037
+ connectionstyle_obj = arrow.get_connectionstyle()
1038
+ posA = canvas.transData.transform(edge_subgraph.nodes[e[0]][pos])
1039
+ path_disp = connectionstyle_obj(posA, posA)
1040
+ path_data = canvas.transData.inverted().transform_path(path_disp)
1041
+ x, y = path_data.vertices[0]
1042
+ canvas.text(
1043
+ x,
1044
+ y,
1045
+ lbl_text,
1046
+ size=lbl.get("size", defaults["edge_label"]["size"]),
1047
+ color=lbl.get("color", defaults["edge_label"]["color"]),
1048
+ family=lbl.get("family", defaults["edge_label"]["family"]),
1049
+ weight=lbl.get("weight", defaults["edge_label"]["weight"]),
1050
+ alpha=lbl.get("alpha", defaults["edge_label"]["alpha"]),
1051
+ horizontalalignment=lbl.get(
1052
+ "h_align", defaults["edge_label"]["h_align"]
1053
+ ),
1054
+ verticalalignment=lbl.get(
1055
+ "v_align", defaults["edge_label"]["v_align"]
1056
+ ),
1057
+ rotation=0,
1058
+ transform=canvas.transData,
1059
+ bbox=lbl.get("bbox", defaults["edge_label"]["bbox"]),
1060
+ zorder=1,
1061
+ )
1062
+ continue
1063
+
1064
+ CurvedArrowText(
1065
+ arrow,
1066
+ lbl_text,
1067
+ size=lbl.get("size", defaults["edge_label"]["size"]),
1068
+ color=lbl.get("color", defaults["edge_label"]["color"]),
1069
+ family=lbl.get("family", defaults["edge_label"]["family"]),
1070
+ weight=lbl.get("weight", defaults["edge_label"]["weight"]),
1071
+ alpha=lbl.get("alpha", defaults["edge_label"]["alpha"]),
1072
+ bbox=lbl.get("bbox", defaults["edge_label"]["bbox"]),
1073
+ horizontalalignment=lbl.get(
1074
+ "h_align", defaults["edge_label"]["h_align"]
1075
+ ),
1076
+ verticalalignment=lbl.get("v_align", defaults["edge_label"]["v_align"]),
1077
+ label_pos=lbl.get("pos", defaults["edge_label"]["pos"]),
1078
+ labels_horizontal=lbl.get("rotate", defaults["edge_label"]["rotate"]),
1079
+ transform=canvas.transData,
1080
+ zorder=1,
1081
+ ax=canvas,
1082
+ )
1083
+
1084
+ # If we had to add an attribute, remove it here
1085
+ if pos == default_display_pos_attr:
1086
+ nx.remove_node_attributes(G, default_display_pos_attr)
1087
+
1088
+ return G
1089
+
1090
+
1091
+ def draw(G, pos=None, ax=None, **kwds):
1092
+ """Draw the graph G with Matplotlib.
1093
+
1094
+ Draw the graph as a simple representation with no node
1095
+ labels or edge labels and using the full Matplotlib figure area
1096
+ and no axis labels by default. See draw_networkx() for more
1097
+ full-featured drawing that allows title, axis labels etc.
1098
+
1099
+ Parameters
1100
+ ----------
1101
+ G : graph
1102
+ A networkx graph
1103
+
1104
+ pos : dictionary, optional
1105
+ A dictionary with nodes as keys and positions as values.
1106
+ If not specified a spring layout positioning will be computed.
1107
+ See :py:mod:`networkx.drawing.layout` for functions that
1108
+ compute node positions.
1109
+
1110
+ ax : Matplotlib Axes object, optional
1111
+ Draw the graph in specified Matplotlib axes.
1112
+
1113
+ kwds : optional keywords
1114
+ See networkx.draw_networkx() for a description of optional keywords.
1115
+
1116
+ Examples
1117
+ --------
1118
+ >>> G = nx.dodecahedral_graph()
1119
+ >>> nx.draw(G)
1120
+ >>> nx.draw(G, pos=nx.spring_layout(G)) # use spring layout
1121
+
1122
+ See Also
1123
+ --------
1124
+ draw_networkx
1125
+ draw_networkx_nodes
1126
+ draw_networkx_edges
1127
+ draw_networkx_labels
1128
+ draw_networkx_edge_labels
1129
+
1130
+ Notes
1131
+ -----
1132
+ This function has the same name as pylab.draw and pyplot.draw
1133
+ so beware when using `from networkx import *`
1134
+
1135
+ since you might overwrite the pylab.draw function.
1136
+
1137
+ With pyplot use
1138
+
1139
+ >>> import matplotlib.pyplot as plt
1140
+ >>> G = nx.dodecahedral_graph()
1141
+ >>> nx.draw(G) # networkx draw()
1142
+ >>> plt.draw() # pyplot draw()
1143
+
1144
+ Also see the NetworkX drawing examples at
1145
+ https://networkx.org/documentation/latest/auto_examples/index.html
1146
+ """
1147
+
1148
+ import matplotlib.pyplot as plt
1149
+
1150
+ if ax is None:
1151
+ cf = plt.gcf()
1152
+ else:
1153
+ cf = ax.get_figure()
1154
+ cf.set_facecolor("w")
1155
+ if ax is None:
1156
+ if cf.axes:
1157
+ ax = cf.gca()
1158
+ else:
1159
+ ax = cf.add_axes((0, 0, 1, 1))
1160
+
1161
+ if "with_labels" not in kwds:
1162
+ kwds["with_labels"] = "labels" in kwds
1163
+
1164
+ draw_networkx(G, pos=pos, ax=ax, **kwds)
1165
+ ax.set_axis_off()
1166
+ plt.draw_if_interactive()
1167
+ return
1168
+
1169
+
1170
+ def draw_networkx(G, pos=None, arrows=None, with_labels=True, **kwds):
1171
+ r"""Draw the graph G using Matplotlib.
1172
+
1173
+ Draw the graph with Matplotlib with options for node positions,
1174
+ labeling, titles, and many other drawing features.
1175
+ See draw() for simple drawing without labels or axes.
1176
+
1177
+ Parameters
1178
+ ----------
1179
+ G : graph
1180
+ A networkx graph
1181
+
1182
+ pos : dictionary, optional
1183
+ A dictionary with nodes as keys and positions as values.
1184
+ If not specified a spring layout positioning will be computed.
1185
+ See :py:mod:`networkx.drawing.layout` for functions that
1186
+ compute node positions.
1187
+
1188
+ arrows : bool or None, optional (default=None)
1189
+ If `None`, directed graphs draw arrowheads with
1190
+ `~matplotlib.patches.FancyArrowPatch`, while undirected graphs draw edges
1191
+ via `~matplotlib.collections.LineCollection` for speed.
1192
+ If `True`, draw arrowheads with FancyArrowPatches (bendable and stylish).
1193
+ If `False`, draw edges using LineCollection (linear and fast).
1194
+ For directed graphs, if True draw arrowheads.
1195
+ Note: Arrows will be the same color as edges.
1196
+
1197
+ arrowstyle : str (default='-\|>' for directed graphs)
1198
+ For directed graphs, choose the style of the arrowsheads.
1199
+ For undirected graphs default to '-'
1200
+
1201
+ See `matplotlib.patches.ArrowStyle` for more options.
1202
+
1203
+ arrowsize : int or list (default=10)
1204
+ For directed graphs, choose the size of the arrow head's length and
1205
+ width. A list of values can be passed in to assign a different size for arrow head's length and width.
1206
+ See `matplotlib.patches.FancyArrowPatch` for attribute `mutation_scale`
1207
+ for more info.
1208
+
1209
+ with_labels : bool (default=True)
1210
+ Set to True to draw labels on the nodes.
1211
+
1212
+ ax : Matplotlib Axes object, optional
1213
+ Draw the graph in the specified Matplotlib axes.
1214
+
1215
+ nodelist : list (default=list(G))
1216
+ Draw only specified nodes
1217
+
1218
+ edgelist : list (default=list(G.edges()))
1219
+ Draw only specified edges
1220
+
1221
+ node_size : scalar or array (default=300)
1222
+ Size of nodes. If an array is specified it must be the
1223
+ same length as nodelist.
1224
+
1225
+ node_color : color or array of colors (default='#1f78b4')
1226
+ Node color. Can be a single color or a sequence of colors with the same
1227
+ length as nodelist. Color can be string or rgb (or rgba) tuple of
1228
+ floats from 0-1. If numeric values are specified they will be
1229
+ mapped to colors using the cmap and vmin,vmax parameters. See
1230
+ matplotlib.scatter for more details.
1231
+
1232
+ node_shape : string (default='o')
1233
+ The shape of the node. Specification is as matplotlib.scatter
1234
+ marker, one of 'so^>v<dph8'.
1235
+
1236
+ alpha : float or None (default=None)
1237
+ The node and edge transparency
1238
+
1239
+ cmap : Matplotlib colormap, optional
1240
+ Colormap for mapping intensities of nodes
1241
+
1242
+ vmin,vmax : float, optional
1243
+ Minimum and maximum for node colormap scaling
1244
+
1245
+ linewidths : scalar or sequence (default=1.0)
1246
+ Line width of symbol border
1247
+
1248
+ width : float or array of floats (default=1.0)
1249
+ Line width of edges
1250
+
1251
+ edge_color : color or array of colors (default='k')
1252
+ Edge color. Can be a single color or a sequence of colors with the same
1253
+ length as edgelist. Color can be string or rgb (or rgba) tuple of
1254
+ floats from 0-1. If numeric values are specified they will be
1255
+ mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters.
1256
+
1257
+ edge_cmap : Matplotlib colormap, optional
1258
+ Colormap for mapping intensities of edges
1259
+
1260
+ edge_vmin,edge_vmax : floats, optional
1261
+ Minimum and maximum for edge colormap scaling
1262
+
1263
+ style : string (default=solid line)
1264
+ Edge line style e.g.: '-', '--', '-.', ':'
1265
+ or words like 'solid' or 'dashed'.
1266
+ (See `matplotlib.patches.FancyArrowPatch`: `linestyle`)
1267
+
1268
+ labels : dictionary (default=None)
1269
+ Node labels in a dictionary of text labels keyed by node
1270
+
1271
+ font_size : int (default=12 for nodes, 10 for edges)
1272
+ Font size for text labels
1273
+
1274
+ font_color : color (default='k' black)
1275
+ Font color string. Color can be string or rgb (or rgba) tuple of
1276
+ floats from 0-1.
1277
+
1278
+ font_weight : string (default='normal')
1279
+ Font weight
1280
+
1281
+ font_family : string (default='sans-serif')
1282
+ Font family
1283
+
1284
+ label : string, optional
1285
+ Label for graph legend
1286
+
1287
+ hide_ticks : bool, optional
1288
+ Hide ticks of axes. When `True` (the default), ticks and ticklabels
1289
+ are removed from the axes. To set ticks and tick labels to the pyplot default,
1290
+ use ``hide_ticks=False``.
1291
+
1292
+ kwds : optional keywords
1293
+ See networkx.draw_networkx_nodes(), networkx.draw_networkx_edges(), and
1294
+ networkx.draw_networkx_labels() for a description of optional keywords.
1295
+
1296
+ Notes
1297
+ -----
1298
+ For directed graphs, arrows are drawn at the head end. Arrows can be
1299
+ turned off with keyword arrows=False.
1300
+
1301
+ Examples
1302
+ --------
1303
+ >>> G = nx.dodecahedral_graph()
1304
+ >>> nx.draw(G)
1305
+ >>> nx.draw(G, pos=nx.spring_layout(G)) # use spring layout
1306
+
1307
+ >>> import matplotlib.pyplot as plt
1308
+ >>> limits = plt.axis("off") # turn off axis
1309
+
1310
+ Also see the NetworkX drawing examples at
1311
+ https://networkx.org/documentation/latest/auto_examples/index.html
1312
+
1313
+ See Also
1314
+ --------
1315
+ draw
1316
+ draw_networkx_nodes
1317
+ draw_networkx_edges
1318
+ draw_networkx_labels
1319
+ draw_networkx_edge_labels
1320
+ """
1321
+ from inspect import signature
1322
+
1323
+ import matplotlib.pyplot as plt
1324
+
1325
+ # Get all valid keywords by inspecting the signatures of draw_networkx_nodes,
1326
+ # draw_networkx_edges, draw_networkx_labels
1327
+
1328
+ valid_node_kwds = signature(draw_networkx_nodes).parameters.keys()
1329
+ valid_edge_kwds = signature(draw_networkx_edges).parameters.keys()
1330
+ valid_label_kwds = signature(draw_networkx_labels).parameters.keys()
1331
+
1332
+ # Create a set with all valid keywords across the three functions and
1333
+ # remove the arguments of this function (draw_networkx)
1334
+ valid_kwds = (valid_node_kwds | valid_edge_kwds | valid_label_kwds) - {
1335
+ "G",
1336
+ "pos",
1337
+ "arrows",
1338
+ "with_labels",
1339
+ }
1340
+
1341
+ if any(k not in valid_kwds for k in kwds):
1342
+ invalid_args = ", ".join([k for k in kwds if k not in valid_kwds])
1343
+ raise ValueError(f"Received invalid argument(s): {invalid_args}")
1344
+
1345
+ node_kwds = {k: v for k, v in kwds.items() if k in valid_node_kwds}
1346
+ edge_kwds = {k: v for k, v in kwds.items() if k in valid_edge_kwds}
1347
+ label_kwds = {k: v for k, v in kwds.items() if k in valid_label_kwds}
1348
+
1349
+ if pos is None:
1350
+ pos = nx.drawing.spring_layout(G) # default to spring layout
1351
+
1352
+ draw_networkx_nodes(G, pos, **node_kwds)
1353
+ draw_networkx_edges(G, pos, arrows=arrows, **edge_kwds)
1354
+ if with_labels:
1355
+ draw_networkx_labels(G, pos, **label_kwds)
1356
+ plt.draw_if_interactive()
1357
+
1358
+
1359
+ def draw_networkx_nodes(
1360
+ G,
1361
+ pos,
1362
+ nodelist=None,
1363
+ node_size=300,
1364
+ node_color="#1f78b4",
1365
+ node_shape="o",
1366
+ alpha=None,
1367
+ cmap=None,
1368
+ vmin=None,
1369
+ vmax=None,
1370
+ ax=None,
1371
+ linewidths=None,
1372
+ edgecolors=None,
1373
+ label=None,
1374
+ margins=None,
1375
+ hide_ticks=True,
1376
+ ):
1377
+ """Draw the nodes of the graph G.
1378
+
1379
+ This draws only the nodes of the graph G.
1380
+
1381
+ Parameters
1382
+ ----------
1383
+ G : graph
1384
+ A networkx graph
1385
+
1386
+ pos : dictionary
1387
+ A dictionary with nodes as keys and positions as values.
1388
+ Positions should be sequences of length 2.
1389
+
1390
+ ax : Matplotlib Axes object, optional
1391
+ Draw the graph in the specified Matplotlib axes.
1392
+
1393
+ nodelist : list (default list(G))
1394
+ Draw only specified nodes
1395
+
1396
+ node_size : scalar or array (default=300)
1397
+ Size of nodes. If an array it must be the same length as nodelist.
1398
+
1399
+ node_color : color or array of colors (default='#1f78b4')
1400
+ Node color. Can be a single color or a sequence of colors with the same
1401
+ length as nodelist. Color can be string or rgb (or rgba) tuple of
1402
+ floats from 0-1. If numeric values are specified they will be
1403
+ mapped to colors using the cmap and vmin,vmax parameters. See
1404
+ matplotlib.scatter for more details.
1405
+
1406
+ node_shape : string (default='o')
1407
+ The shape of the node. Specification is as matplotlib.scatter
1408
+ marker, one of 'so^>v<dph8'.
1409
+
1410
+ alpha : float or array of floats (default=None)
1411
+ The node transparency. This can be a single alpha value,
1412
+ in which case it will be applied to all the nodes of color. Otherwise,
1413
+ if it is an array, the elements of alpha will be applied to the colors
1414
+ in order (cycling through alpha multiple times if necessary).
1415
+
1416
+ cmap : Matplotlib colormap (default=None)
1417
+ Colormap for mapping intensities of nodes
1418
+
1419
+ vmin,vmax : floats or None (default=None)
1420
+ Minimum and maximum for node colormap scaling
1421
+
1422
+ linewidths : [None | scalar | sequence] (default=1.0)
1423
+ Line width of symbol border
1424
+
1425
+ edgecolors : [None | scalar | sequence] (default = node_color)
1426
+ Colors of node borders. Can be a single color or a sequence of colors with the
1427
+ same length as nodelist. Color can be string or rgb (or rgba) tuple of floats
1428
+ from 0-1. If numeric values are specified they will be mapped to colors
1429
+ using the cmap and vmin,vmax parameters. See `~matplotlib.pyplot.scatter` for more details.
1430
+
1431
+ label : [None | string]
1432
+ Label for legend
1433
+
1434
+ margins : float or 2-tuple, optional
1435
+ Sets the padding for axis autoscaling. Increase margin to prevent
1436
+ clipping for nodes that are near the edges of an image. Values should
1437
+ be in the range ``[0, 1]``. See :meth:`matplotlib.axes.Axes.margins`
1438
+ for details. The default is `None`, which uses the Matplotlib default.
1439
+
1440
+ hide_ticks : bool, optional
1441
+ Hide ticks of axes. When `True` (the default), ticks and ticklabels
1442
+ are removed from the axes. To set ticks and tick labels to the pyplot default,
1443
+ use ``hide_ticks=False``.
1444
+
1445
+ Returns
1446
+ -------
1447
+ matplotlib.collections.PathCollection
1448
+ `PathCollection` of the nodes.
1449
+
1450
+ Examples
1451
+ --------
1452
+ >>> G = nx.dodecahedral_graph()
1453
+ >>> nodes = nx.draw_networkx_nodes(G, pos=nx.spring_layout(G))
1454
+
1455
+ Also see the NetworkX drawing examples at
1456
+ https://networkx.org/documentation/latest/auto_examples/index.html
1457
+
1458
+ See Also
1459
+ --------
1460
+ draw
1461
+ draw_networkx
1462
+ draw_networkx_edges
1463
+ draw_networkx_labels
1464
+ draw_networkx_edge_labels
1465
+ """
1466
+ from collections.abc import Iterable
1467
+
1468
+ import matplotlib as mpl
1469
+ import matplotlib.collections # call as mpl.collections
1470
+ import matplotlib.pyplot as plt
1471
+ import numpy as np
1472
+
1473
+ if ax is None:
1474
+ ax = plt.gca()
1475
+
1476
+ if nodelist is None:
1477
+ nodelist = list(G)
1478
+
1479
+ if len(nodelist) == 0: # empty nodelist, no drawing
1480
+ return mpl.collections.PathCollection(None)
1481
+
1482
+ try:
1483
+ xy = np.asarray([pos[v] for v in nodelist])
1484
+ except KeyError as err:
1485
+ raise nx.NetworkXError(f"Node {err} has no position.") from err
1486
+
1487
+ if isinstance(alpha, Iterable):
1488
+ node_color = apply_alpha(node_color, alpha, nodelist, cmap, vmin, vmax)
1489
+ alpha = None
1490
+
1491
+ if not isinstance(node_shape, np.ndarray) and not isinstance(node_shape, list):
1492
+ node_shape = np.array([node_shape for _ in range(len(nodelist))])
1493
+ elif isinstance(node_shape, list):
1494
+ node_shape = np.asarray(node_shape)
1495
+
1496
+ for shape in np.unique(node_shape):
1497
+ node_collection = ax.scatter(
1498
+ xy[node_shape == shape, 0],
1499
+ xy[node_shape == shape, 1],
1500
+ s=node_size,
1501
+ c=node_color,
1502
+ marker=shape,
1503
+ cmap=cmap,
1504
+ vmin=vmin,
1505
+ vmax=vmax,
1506
+ alpha=alpha,
1507
+ linewidths=linewidths,
1508
+ edgecolors=edgecolors,
1509
+ label=label,
1510
+ )
1511
+ if hide_ticks:
1512
+ ax.tick_params(
1513
+ axis="both",
1514
+ which="both",
1515
+ bottom=False,
1516
+ left=False,
1517
+ labelbottom=False,
1518
+ labelleft=False,
1519
+ )
1520
+
1521
+ if margins is not None:
1522
+ if isinstance(margins, Iterable):
1523
+ ax.margins(*margins)
1524
+ else:
1525
+ ax.margins(margins)
1526
+
1527
+ node_collection.set_zorder(2)
1528
+ return node_collection
1529
+
1530
+
1531
+ class FancyArrowFactory:
1532
+ """Draw arrows with `matplotlib.patches.FancyarrowPatch`"""
1533
+
1534
+ class ConnectionStyleFactory:
1535
+ def __init__(self, connectionstyles, selfloop_height, ax=None):
1536
+ import matplotlib as mpl
1537
+ import matplotlib.path # call as mpl.path
1538
+ import numpy as np
1539
+
1540
+ self.ax = ax
1541
+ self.mpl = mpl
1542
+ self.np = np
1543
+ self.base_connection_styles = [
1544
+ mpl.patches.ConnectionStyle(cs) for cs in connectionstyles
1545
+ ]
1546
+ self.n = len(self.base_connection_styles)
1547
+ self.selfloop_height = selfloop_height
1548
+
1549
+ def curved(self, edge_index):
1550
+ return self.base_connection_styles[edge_index % self.n]
1551
+
1552
+ def self_loop(self, edge_index):
1553
+ def self_loop_connection(posA, posB, *args, **kwargs):
1554
+ if not self.np.all(posA == posB):
1555
+ raise nx.NetworkXError(
1556
+ "`self_loop` connection style method"
1557
+ "is only to be used for self-loops"
1558
+ )
1559
+ # this is called with _screen space_ values
1560
+ # so convert back to data space
1561
+ data_loc = self.ax.transData.inverted().transform(posA)
1562
+ v_shift = 0.1 * self.selfloop_height
1563
+ h_shift = v_shift * 0.5
1564
+ # put the top of the loop first so arrow is not hidden by node
1565
+ path = self.np.asarray(
1566
+ [
1567
+ # 1
1568
+ [0, v_shift],
1569
+ # 4 4 4
1570
+ [h_shift, v_shift],
1571
+ [h_shift, 0],
1572
+ [0, 0],
1573
+ # 4 4 4
1574
+ [-h_shift, 0],
1575
+ [-h_shift, v_shift],
1576
+ [0, v_shift],
1577
+ ]
1578
+ )
1579
+ # Rotate self loop 90 deg. if more than 1
1580
+ # This will allow for maximum of 4 visible self loops
1581
+ if edge_index % 4:
1582
+ x, y = path.T
1583
+ for _ in range(edge_index % 4):
1584
+ x, y = y, -x
1585
+ path = self.np.array([x, y]).T
1586
+ return self.mpl.path.Path(
1587
+ self.ax.transData.transform(data_loc + path), [1, 4, 4, 4, 4, 4, 4]
1588
+ )
1589
+
1590
+ return self_loop_connection
1591
+
1592
+ def __init__(
1593
+ self,
1594
+ edge_pos,
1595
+ edgelist,
1596
+ nodelist,
1597
+ edge_indices,
1598
+ node_size,
1599
+ selfloop_height,
1600
+ connectionstyle="arc3",
1601
+ node_shape="o",
1602
+ arrowstyle="-",
1603
+ arrowsize=10,
1604
+ edge_color="k",
1605
+ alpha=None,
1606
+ linewidth=1.0,
1607
+ style="solid",
1608
+ min_source_margin=0,
1609
+ min_target_margin=0,
1610
+ ax=None,
1611
+ ):
1612
+ import matplotlib as mpl
1613
+ import matplotlib.patches # call as mpl.patches
1614
+ import matplotlib.pyplot as plt
1615
+ import numpy as np
1616
+
1617
+ if isinstance(connectionstyle, str):
1618
+ connectionstyle = [connectionstyle]
1619
+ elif np.iterable(connectionstyle):
1620
+ connectionstyle = list(connectionstyle)
1621
+ else:
1622
+ msg = "ConnectionStyleFactory arg `connectionstyle` must be str or iterable"
1623
+ raise nx.NetworkXError(msg)
1624
+ self.ax = ax
1625
+ self.mpl = mpl
1626
+ self.np = np
1627
+ self.edge_pos = edge_pos
1628
+ self.edgelist = edgelist
1629
+ self.nodelist = nodelist
1630
+ self.node_shape = node_shape
1631
+ self.min_source_margin = min_source_margin
1632
+ self.min_target_margin = min_target_margin
1633
+ self.edge_indices = edge_indices
1634
+ self.node_size = node_size
1635
+ self.connectionstyle_factory = self.ConnectionStyleFactory(
1636
+ connectionstyle, selfloop_height, ax
1637
+ )
1638
+ self.arrowstyle = arrowstyle
1639
+ self.arrowsize = arrowsize
1640
+ self.arrow_colors = mpl.colors.colorConverter.to_rgba_array(edge_color, alpha)
1641
+ self.linewidth = linewidth
1642
+ self.style = style
1643
+ if isinstance(arrowsize, list) and len(arrowsize) != len(edge_pos):
1644
+ raise ValueError("arrowsize should have the same length as edgelist")
1645
+
1646
+ def __call__(self, i):
1647
+ (x1, y1), (x2, y2) = self.edge_pos[i]
1648
+ shrink_source = 0 # space from source to tail
1649
+ shrink_target = 0 # space from head to target
1650
+ if (
1651
+ self.np.iterable(self.min_source_margin)
1652
+ and not isinstance(self.min_source_margin, str)
1653
+ and not isinstance(self.min_source_margin, tuple)
1654
+ ):
1655
+ min_source_margin = self.min_source_margin[i]
1656
+ else:
1657
+ min_source_margin = self.min_source_margin
1658
+
1659
+ if (
1660
+ self.np.iterable(self.min_target_margin)
1661
+ and not isinstance(self.min_target_margin, str)
1662
+ and not isinstance(self.min_target_margin, tuple)
1663
+ ):
1664
+ min_target_margin = self.min_target_margin[i]
1665
+ else:
1666
+ min_target_margin = self.min_target_margin
1667
+
1668
+ if self.np.iterable(self.node_size): # many node sizes
1669
+ source, target = self.edgelist[i][:2]
1670
+ source_node_size = self.node_size[self.nodelist.index(source)]
1671
+ target_node_size = self.node_size[self.nodelist.index(target)]
1672
+ shrink_source = self.to_marker_edge(source_node_size, self.node_shape)
1673
+ shrink_target = self.to_marker_edge(target_node_size, self.node_shape)
1674
+ else:
1675
+ shrink_source = self.to_marker_edge(self.node_size, self.node_shape)
1676
+ shrink_target = shrink_source
1677
+ shrink_source = max(shrink_source, min_source_margin)
1678
+ shrink_target = max(shrink_target, min_target_margin)
1679
+
1680
+ # scale factor of arrow head
1681
+ if isinstance(self.arrowsize, list):
1682
+ mutation_scale = self.arrowsize[i]
1683
+ else:
1684
+ mutation_scale = self.arrowsize
1685
+
1686
+ if len(self.arrow_colors) > i:
1687
+ arrow_color = self.arrow_colors[i]
1688
+ elif len(self.arrow_colors) == 1:
1689
+ arrow_color = self.arrow_colors[0]
1690
+ else: # Cycle through colors
1691
+ arrow_color = self.arrow_colors[i % len(self.arrow_colors)]
1692
+
1693
+ if self.np.iterable(self.linewidth):
1694
+ if len(self.linewidth) > i:
1695
+ linewidth = self.linewidth[i]
1696
+ else:
1697
+ linewidth = self.linewidth[i % len(self.linewidth)]
1698
+ else:
1699
+ linewidth = self.linewidth
1700
+
1701
+ if (
1702
+ self.np.iterable(self.style)
1703
+ and not isinstance(self.style, str)
1704
+ and not isinstance(self.style, tuple)
1705
+ ):
1706
+ if len(self.style) > i:
1707
+ linestyle = self.style[i]
1708
+ else: # Cycle through styles
1709
+ linestyle = self.style[i % len(self.style)]
1710
+ else:
1711
+ linestyle = self.style
1712
+
1713
+ if x1 == x2 and y1 == y2:
1714
+ connectionstyle = self.connectionstyle_factory.self_loop(
1715
+ self.edge_indices[i]
1716
+ )
1717
+ else:
1718
+ connectionstyle = self.connectionstyle_factory.curved(self.edge_indices[i])
1719
+
1720
+ if (
1721
+ self.np.iterable(self.arrowstyle)
1722
+ and not isinstance(self.arrowstyle, str)
1723
+ and not isinstance(self.arrowstyle, tuple)
1724
+ ):
1725
+ arrowstyle = self.arrowstyle[i]
1726
+ else:
1727
+ arrowstyle = self.arrowstyle
1728
+
1729
+ return self.mpl.patches.FancyArrowPatch(
1730
+ (x1, y1),
1731
+ (x2, y2),
1732
+ arrowstyle=arrowstyle,
1733
+ shrinkA=shrink_source,
1734
+ shrinkB=shrink_target,
1735
+ mutation_scale=mutation_scale,
1736
+ color=arrow_color,
1737
+ linewidth=linewidth,
1738
+ connectionstyle=connectionstyle,
1739
+ linestyle=linestyle,
1740
+ zorder=1, # arrows go behind nodes
1741
+ )
1742
+
1743
+ def to_marker_edge(self, marker_size, marker):
1744
+ if marker in "s^>v<d": # `large` markers need extra space
1745
+ return self.np.sqrt(2 * marker_size) / 2
1746
+ else:
1747
+ return self.np.sqrt(marker_size) / 2
1748
+
1749
+
1750
+ def draw_networkx_edges(
1751
+ G,
1752
+ pos,
1753
+ edgelist=None,
1754
+ width=1.0,
1755
+ edge_color="k",
1756
+ style="solid",
1757
+ alpha=None,
1758
+ arrowstyle=None,
1759
+ arrowsize=10,
1760
+ edge_cmap=None,
1761
+ edge_vmin=None,
1762
+ edge_vmax=None,
1763
+ ax=None,
1764
+ arrows=None,
1765
+ label=None,
1766
+ node_size=300,
1767
+ nodelist=None,
1768
+ node_shape="o",
1769
+ connectionstyle="arc3",
1770
+ min_source_margin=0,
1771
+ min_target_margin=0,
1772
+ hide_ticks=True,
1773
+ ):
1774
+ r"""Draw the edges of the graph G.
1775
+
1776
+ This draws only the edges of the graph G.
1777
+
1778
+ Parameters
1779
+ ----------
1780
+ G : graph
1781
+ A networkx graph
1782
+
1783
+ pos : dictionary
1784
+ A dictionary with nodes as keys and positions as values.
1785
+ Positions should be sequences of length 2.
1786
+
1787
+ edgelist : collection of edge tuples (default=G.edges())
1788
+ Draw only specified edges
1789
+
1790
+ width : float or array of floats (default=1.0)
1791
+ Line width of edges
1792
+
1793
+ edge_color : color or array of colors (default='k')
1794
+ Edge color. Can be a single color or a sequence of colors with the same
1795
+ length as edgelist. Color can be string or rgb (or rgba) tuple of
1796
+ floats from 0-1. If numeric values are specified they will be
1797
+ mapped to colors using the edge_cmap and edge_vmin,edge_vmax parameters.
1798
+
1799
+ style : string or array of strings (default='solid')
1800
+ Edge line style e.g.: '-', '--', '-.', ':'
1801
+ or words like 'solid' or 'dashed'.
1802
+ Can be a single style or a sequence of styles with the same
1803
+ length as the edge list.
1804
+ If less styles than edges are given the styles will cycle.
1805
+ If more styles than edges are given the styles will be used sequentially
1806
+ and not be exhausted.
1807
+ Also, `(offset, onoffseq)` tuples can be used as style instead of a strings.
1808
+ (See `matplotlib.patches.FancyArrowPatch`: `linestyle`)
1809
+
1810
+ alpha : float or array of floats (default=None)
1811
+ The edge transparency. This can be a single alpha value,
1812
+ in which case it will be applied to all specified edges. Otherwise,
1813
+ if it is an array, the elements of alpha will be applied to the colors
1814
+ in order (cycling through alpha multiple times if necessary).
1815
+
1816
+ edge_cmap : Matplotlib colormap, optional
1817
+ Colormap for mapping intensities of edges
1818
+
1819
+ edge_vmin,edge_vmax : floats, optional
1820
+ Minimum and maximum for edge colormap scaling
1821
+
1822
+ ax : Matplotlib Axes object, optional
1823
+ Draw the graph in the specified Matplotlib axes.
1824
+
1825
+ arrows : bool or None, optional (default=None)
1826
+ If `None`, directed graphs draw arrowheads with
1827
+ `~matplotlib.patches.FancyArrowPatch`, while undirected graphs draw edges
1828
+ via `~matplotlib.collections.LineCollection` for speed.
1829
+ If `True`, draw arrowheads with FancyArrowPatches (bendable and stylish).
1830
+ If `False`, draw edges using LineCollection (linear and fast).
1831
+
1832
+ Note: Arrowheads will be the same color as edges.
1833
+
1834
+ arrowstyle : str or list of strs (default='-\|>' for directed graphs)
1835
+ For directed graphs and `arrows==True` defaults to '-\|>',
1836
+ For undirected graphs default to '-'.
1837
+
1838
+ See `matplotlib.patches.ArrowStyle` for more options.
1839
+
1840
+ arrowsize : int or list of ints(default=10)
1841
+ For directed graphs, choose the size of the arrow head's length and
1842
+ width. See `matplotlib.patches.FancyArrowPatch` for attribute
1843
+ `mutation_scale` for more info.
1844
+
1845
+ connectionstyle : string or iterable of strings (default="arc3")
1846
+ Pass the connectionstyle parameter to create curved arc of rounding
1847
+ radius rad. For example, connectionstyle='arc3,rad=0.2'.
1848
+ See `matplotlib.patches.ConnectionStyle` and
1849
+ `matplotlib.patches.FancyArrowPatch` for more info.
1850
+ If Iterable, index indicates i'th edge key of MultiGraph
1851
+
1852
+ node_size : scalar or array (default=300)
1853
+ Size of nodes. Though the nodes are not drawn with this function, the
1854
+ node size is used in determining edge positioning.
1855
+
1856
+ nodelist : list, optional (default=G.nodes())
1857
+ This provides the node order for the `node_size` array (if it is an array).
1858
+
1859
+ node_shape : string (default='o')
1860
+ The marker used for nodes, used in determining edge positioning.
1861
+ Specification is as a `matplotlib.markers` marker, e.g. one of 'so^>v<dph8'.
1862
+
1863
+ label : None or string
1864
+ Label for legend
1865
+
1866
+ min_source_margin : int or list of ints (default=0)
1867
+ The minimum margin (gap) at the beginning of the edge at the source.
1868
+
1869
+ min_target_margin : int or list of ints (default=0)
1870
+ The minimum margin (gap) at the end of the edge at the target.
1871
+
1872
+ hide_ticks : bool, optional
1873
+ Hide ticks of axes. When `True` (the default), ticks and ticklabels
1874
+ are removed from the axes. To set ticks and tick labels to the pyplot default,
1875
+ use ``hide_ticks=False``.
1876
+
1877
+ Returns
1878
+ -------
1879
+ matplotlib.collections.LineCollection or a list of matplotlib.patches.FancyArrowPatch
1880
+ If ``arrows=True``, a list of FancyArrowPatches is returned.
1881
+ If ``arrows=False``, a LineCollection is returned.
1882
+ If ``arrows=None`` (the default), then a LineCollection is returned if
1883
+ `G` is undirected, otherwise returns a list of FancyArrowPatches.
1884
+
1885
+ Notes
1886
+ -----
1887
+ For directed graphs, arrows are drawn at the head end. Arrows can be
1888
+ turned off with keyword arrows=False or by passing an arrowstyle without
1889
+ an arrow on the end.
1890
+
1891
+ Be sure to include `node_size` as a keyword argument; arrows are
1892
+ drawn considering the size of nodes.
1893
+
1894
+ Self-loops are always drawn with `~matplotlib.patches.FancyArrowPatch`
1895
+ regardless of the value of `arrows` or whether `G` is directed.
1896
+ When ``arrows=False`` or ``arrows=None`` and `G` is undirected, the
1897
+ FancyArrowPatches corresponding to the self-loops are not explicitly
1898
+ returned. They should instead be accessed via the ``Axes.patches``
1899
+ attribute (see examples).
1900
+
1901
+ Examples
1902
+ --------
1903
+ >>> G = nx.dodecahedral_graph()
1904
+ >>> edges = nx.draw_networkx_edges(G, pos=nx.spring_layout(G))
1905
+
1906
+ >>> G = nx.DiGraph()
1907
+ >>> G.add_edges_from([(1, 2), (1, 3), (2, 3)])
1908
+ >>> arcs = nx.draw_networkx_edges(G, pos=nx.spring_layout(G))
1909
+ >>> alphas = [0.3, 0.4, 0.5]
1910
+ >>> for i, arc in enumerate(arcs): # change alpha values of arcs
1911
+ ... arc.set_alpha(alphas[i])
1912
+
1913
+ The FancyArrowPatches corresponding to self-loops are not always
1914
+ returned, but can always be accessed via the ``patches`` attribute of the
1915
+ `matplotlib.Axes` object.
1916
+
1917
+ >>> import matplotlib.pyplot as plt
1918
+ >>> fig, ax = plt.subplots()
1919
+ >>> G = nx.Graph([(0, 1), (0, 0)]) # Self-loop at node 0
1920
+ >>> edge_collection = nx.draw_networkx_edges(G, pos=nx.circular_layout(G), ax=ax)
1921
+ >>> self_loop_fap = ax.patches[0]
1922
+
1923
+ Also see the NetworkX drawing examples at
1924
+ https://networkx.org/documentation/latest/auto_examples/index.html
1925
+
1926
+ See Also
1927
+ --------
1928
+ draw
1929
+ draw_networkx
1930
+ draw_networkx_nodes
1931
+ draw_networkx_labels
1932
+ draw_networkx_edge_labels
1933
+
1934
+ """
1935
+ import warnings
1936
+
1937
+ import matplotlib as mpl
1938
+ import matplotlib.collections # call as mpl.collections
1939
+ import matplotlib.colors # call as mpl.colors
1940
+ import matplotlib.pyplot as plt
1941
+ import numpy as np
1942
+
1943
+ # The default behavior is to use LineCollection to draw edges for
1944
+ # undirected graphs (for performance reasons) and use FancyArrowPatches
1945
+ # for directed graphs.
1946
+ # The `arrows` keyword can be used to override the default behavior
1947
+ if arrows is None:
1948
+ use_linecollection = not (G.is_directed() or G.is_multigraph())
1949
+ else:
1950
+ if not isinstance(arrows, bool):
1951
+ raise TypeError("Argument `arrows` must be of type bool or None")
1952
+ use_linecollection = not arrows
1953
+
1954
+ if isinstance(connectionstyle, str):
1955
+ connectionstyle = [connectionstyle]
1956
+ elif np.iterable(connectionstyle):
1957
+ connectionstyle = list(connectionstyle)
1958
+ else:
1959
+ msg = "draw_networkx_edges arg `connectionstyle` must be str or iterable"
1960
+ raise nx.NetworkXError(msg)
1961
+
1962
+ # Some kwargs only apply to FancyArrowPatches. Warn users when they use
1963
+ # non-default values for these kwargs when LineCollection is being used
1964
+ # instead of silently ignoring the specified option
1965
+ if use_linecollection:
1966
+ msg = (
1967
+ "\n\nThe {0} keyword argument is not applicable when drawing edges\n"
1968
+ "with LineCollection.\n\n"
1969
+ "To make this warning go away, either specify `arrows=True` to\n"
1970
+ "force FancyArrowPatches or use the default values.\n"
1971
+ "Note that using FancyArrowPatches may be slow for large graphs.\n"
1972
+ )
1973
+ if arrowstyle is not None:
1974
+ warnings.warn(msg.format("arrowstyle"), category=UserWarning, stacklevel=2)
1975
+ if arrowsize != 10:
1976
+ warnings.warn(msg.format("arrowsize"), category=UserWarning, stacklevel=2)
1977
+ if min_source_margin != 0:
1978
+ warnings.warn(
1979
+ msg.format("min_source_margin"), category=UserWarning, stacklevel=2
1980
+ )
1981
+ if min_target_margin != 0:
1982
+ warnings.warn(
1983
+ msg.format("min_target_margin"), category=UserWarning, stacklevel=2
1984
+ )
1985
+ if any(cs != "arc3" for cs in connectionstyle):
1986
+ warnings.warn(
1987
+ msg.format("connectionstyle"), category=UserWarning, stacklevel=2
1988
+ )
1989
+
1990
+ # NOTE: Arrowstyle modification must occur after the warnings section
1991
+ if arrowstyle is None:
1992
+ arrowstyle = "-|>" if G.is_directed() else "-"
1993
+
1994
+ if ax is None:
1995
+ ax = plt.gca()
1996
+
1997
+ if edgelist is None:
1998
+ edgelist = list(G.edges) # (u, v, k) for multigraph (u, v) otherwise
1999
+
2000
+ if len(edgelist):
2001
+ if G.is_multigraph():
2002
+ key_count = collections.defaultdict(lambda: itertools.count(0))
2003
+ edge_indices = [next(key_count[tuple(e[:2])]) for e in edgelist]
2004
+ else:
2005
+ edge_indices = [0] * len(edgelist)
2006
+ else: # no edges!
2007
+ return []
2008
+
2009
+ if nodelist is None:
2010
+ nodelist = list(G.nodes())
2011
+
2012
+ # FancyArrowPatch handles color=None different from LineCollection
2013
+ if edge_color is None:
2014
+ edge_color = "k"
2015
+
2016
+ # set edge positions
2017
+ edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist])
2018
+
2019
+ # Check if edge_color is an array of floats and map to edge_cmap.
2020
+ # This is the only case handled differently from matplotlib
2021
+ if (
2022
+ np.iterable(edge_color)
2023
+ and (len(edge_color) == len(edge_pos))
2024
+ and np.all([isinstance(c, Number) for c in edge_color])
2025
+ ):
2026
+ if edge_cmap is not None:
2027
+ assert isinstance(edge_cmap, mpl.colors.Colormap)
2028
+ else:
2029
+ edge_cmap = plt.get_cmap()
2030
+ if edge_vmin is None:
2031
+ edge_vmin = min(edge_color)
2032
+ if edge_vmax is None:
2033
+ edge_vmax = max(edge_color)
2034
+ color_normal = mpl.colors.Normalize(vmin=edge_vmin, vmax=edge_vmax)
2035
+ edge_color = [edge_cmap(color_normal(e)) for e in edge_color]
2036
+
2037
+ # compute initial view
2038
+ minx = np.amin(np.ravel(edge_pos[:, :, 0]))
2039
+ maxx = np.amax(np.ravel(edge_pos[:, :, 0]))
2040
+ miny = np.amin(np.ravel(edge_pos[:, :, 1]))
2041
+ maxy = np.amax(np.ravel(edge_pos[:, :, 1]))
2042
+ w = maxx - minx
2043
+ h = maxy - miny
2044
+
2045
+ # Self-loops are scaled by view extent, except in cases the extent
2046
+ # is 0, e.g. for a single node. In this case, fall back to scaling
2047
+ # by the maximum node size
2048
+ selfloop_height = h if h != 0 else 0.005 * np.array(node_size).max()
2049
+ fancy_arrow_factory = FancyArrowFactory(
2050
+ edge_pos,
2051
+ edgelist,
2052
+ nodelist,
2053
+ edge_indices,
2054
+ node_size,
2055
+ selfloop_height,
2056
+ connectionstyle,
2057
+ node_shape,
2058
+ arrowstyle,
2059
+ arrowsize,
2060
+ edge_color,
2061
+ alpha,
2062
+ width,
2063
+ style,
2064
+ min_source_margin,
2065
+ min_target_margin,
2066
+ ax=ax,
2067
+ )
2068
+
2069
+ # Draw the edges
2070
+ if use_linecollection:
2071
+ edge_collection = mpl.collections.LineCollection(
2072
+ edge_pos,
2073
+ colors=edge_color,
2074
+ linewidths=width,
2075
+ antialiaseds=(1,),
2076
+ linestyle=style,
2077
+ alpha=alpha,
2078
+ )
2079
+ edge_collection.set_cmap(edge_cmap)
2080
+ edge_collection.set_clim(edge_vmin, edge_vmax)
2081
+ edge_collection.set_zorder(1) # edges go behind nodes
2082
+ edge_collection.set_label(label)
2083
+ ax.add_collection(edge_collection)
2084
+ edge_viz_obj = edge_collection
2085
+
2086
+ # Make sure selfloop edges are also drawn
2087
+ # ---------------------------------------
2088
+ selfloops_to_draw = [loop for loop in nx.selfloop_edges(G) if loop in edgelist]
2089
+ if selfloops_to_draw:
2090
+ edgelist_tuple = list(map(tuple, edgelist))
2091
+ arrow_collection = []
2092
+ for loop in selfloops_to_draw:
2093
+ i = edgelist_tuple.index(loop)
2094
+ arrow = fancy_arrow_factory(i)
2095
+ arrow_collection.append(arrow)
2096
+ ax.add_patch(arrow)
2097
+ else:
2098
+ edge_viz_obj = []
2099
+ for i in range(len(edgelist)):
2100
+ arrow = fancy_arrow_factory(i)
2101
+ ax.add_patch(arrow)
2102
+ edge_viz_obj.append(arrow)
2103
+
2104
+ # update view after drawing
2105
+ padx, pady = 0.05 * w, 0.05 * h
2106
+ corners = (minx - padx, miny - pady), (maxx + padx, maxy + pady)
2107
+ ax.update_datalim(corners)
2108
+ ax.autoscale_view()
2109
+
2110
+ if hide_ticks:
2111
+ ax.tick_params(
2112
+ axis="both",
2113
+ which="both",
2114
+ bottom=False,
2115
+ left=False,
2116
+ labelbottom=False,
2117
+ labelleft=False,
2118
+ )
2119
+
2120
+ return edge_viz_obj
2121
+
2122
+
2123
+ def draw_networkx_labels(
2124
+ G,
2125
+ pos,
2126
+ labels=None,
2127
+ font_size=12,
2128
+ font_color="k",
2129
+ font_family="sans-serif",
2130
+ font_weight="normal",
2131
+ alpha=None,
2132
+ bbox=None,
2133
+ horizontalalignment="center",
2134
+ verticalalignment="center",
2135
+ ax=None,
2136
+ clip_on=True,
2137
+ hide_ticks=True,
2138
+ ):
2139
+ """Draw node labels on the graph G.
2140
+
2141
+ Parameters
2142
+ ----------
2143
+ G : graph
2144
+ A networkx graph
2145
+
2146
+ pos : dictionary
2147
+ A dictionary with nodes as keys and positions as values.
2148
+ Positions should be sequences of length 2.
2149
+
2150
+ labels : dictionary (default={n: n for n in G})
2151
+ Node labels in a dictionary of text labels keyed by node.
2152
+ Node-keys in labels should appear as keys in `pos`.
2153
+ If needed use: `{n:lab for n,lab in labels.items() if n in pos}`
2154
+
2155
+ font_size : int or dictionary of nodes to ints (default=12)
2156
+ Font size for text labels.
2157
+
2158
+ font_color : color or dictionary of nodes to colors (default='k' black)
2159
+ Font color string. Color can be string or rgb (or rgba) tuple of
2160
+ floats from 0-1.
2161
+
2162
+ font_weight : string or dictionary of nodes to strings (default='normal')
2163
+ Font weight.
2164
+
2165
+ font_family : string or dictionary of nodes to strings (default='sans-serif')
2166
+ Font family.
2167
+
2168
+ alpha : float or None or dictionary of nodes to floats (default=None)
2169
+ The text transparency.
2170
+
2171
+ bbox : Matplotlib bbox, (default is Matplotlib's ax.text default)
2172
+ Specify text box properties (e.g. shape, color etc.) for node labels.
2173
+
2174
+ horizontalalignment : string or array of strings (default='center')
2175
+ Horizontal alignment {'center', 'right', 'left'}. If an array is
2176
+ specified it must be the same length as `nodelist`.
2177
+
2178
+ verticalalignment : string (default='center')
2179
+ Vertical alignment {'center', 'top', 'bottom', 'baseline', 'center_baseline'}.
2180
+ If an array is specified it must be the same length as `nodelist`.
2181
+
2182
+ ax : Matplotlib Axes object, optional
2183
+ Draw the graph in the specified Matplotlib axes.
2184
+
2185
+ clip_on : bool (default=True)
2186
+ Turn on clipping of node labels at axis boundaries
2187
+
2188
+ hide_ticks : bool, optional
2189
+ Hide ticks of axes. When `True` (the default), ticks and ticklabels
2190
+ are removed from the axes. To set ticks and tick labels to the pyplot default,
2191
+ use ``hide_ticks=False``.
2192
+
2193
+ Returns
2194
+ -------
2195
+ dict
2196
+ `dict` of labels keyed on the nodes
2197
+
2198
+ Examples
2199
+ --------
2200
+ >>> G = nx.dodecahedral_graph()
2201
+ >>> labels = nx.draw_networkx_labels(G, pos=nx.spring_layout(G))
2202
+
2203
+ Also see the NetworkX drawing examples at
2204
+ https://networkx.org/documentation/latest/auto_examples/index.html
2205
+
2206
+ See Also
2207
+ --------
2208
+ draw
2209
+ draw_networkx
2210
+ draw_networkx_nodes
2211
+ draw_networkx_edges
2212
+ draw_networkx_edge_labels
2213
+ """
2214
+ import matplotlib.pyplot as plt
2215
+
2216
+ if ax is None:
2217
+ ax = plt.gca()
2218
+
2219
+ if labels is None:
2220
+ labels = {n: n for n in G.nodes()}
2221
+
2222
+ individual_params = set()
2223
+
2224
+ def check_individual_params(p_value, p_name):
2225
+ if isinstance(p_value, dict):
2226
+ if len(p_value) != len(labels):
2227
+ raise ValueError(f"{p_name} must have the same length as labels.")
2228
+ individual_params.add(p_name)
2229
+
2230
+ def get_param_value(node, p_value, p_name):
2231
+ if p_name in individual_params:
2232
+ return p_value[node]
2233
+ return p_value
2234
+
2235
+ check_individual_params(font_size, "font_size")
2236
+ check_individual_params(font_color, "font_color")
2237
+ check_individual_params(font_weight, "font_weight")
2238
+ check_individual_params(font_family, "font_family")
2239
+ check_individual_params(alpha, "alpha")
2240
+
2241
+ text_items = {} # there is no text collection so we'll fake one
2242
+ for n, label in labels.items():
2243
+ (x, y) = pos[n]
2244
+ if not isinstance(label, str):
2245
+ label = str(label) # this makes "1" and 1 labeled the same
2246
+ t = ax.text(
2247
+ x,
2248
+ y,
2249
+ label,
2250
+ size=get_param_value(n, font_size, "font_size"),
2251
+ color=get_param_value(n, font_color, "font_color"),
2252
+ family=get_param_value(n, font_family, "font_family"),
2253
+ weight=get_param_value(n, font_weight, "font_weight"),
2254
+ alpha=get_param_value(n, alpha, "alpha"),
2255
+ horizontalalignment=horizontalalignment,
2256
+ verticalalignment=verticalalignment,
2257
+ transform=ax.transData,
2258
+ bbox=bbox,
2259
+ clip_on=clip_on,
2260
+ )
2261
+ text_items[n] = t
2262
+
2263
+ if hide_ticks:
2264
+ ax.tick_params(
2265
+ axis="both",
2266
+ which="both",
2267
+ bottom=False,
2268
+ left=False,
2269
+ labelbottom=False,
2270
+ labelleft=False,
2271
+ )
2272
+
2273
+ return text_items
2274
+
2275
+
2276
+ def draw_networkx_edge_labels(
2277
+ G,
2278
+ pos,
2279
+ edge_labels=None,
2280
+ label_pos=0.5,
2281
+ font_size=10,
2282
+ font_color="k",
2283
+ font_family="sans-serif",
2284
+ font_weight="normal",
2285
+ alpha=None,
2286
+ bbox=None,
2287
+ horizontalalignment="center",
2288
+ verticalalignment="center",
2289
+ ax=None,
2290
+ rotate=True,
2291
+ clip_on=True,
2292
+ node_size=300,
2293
+ nodelist=None,
2294
+ connectionstyle="arc3",
2295
+ hide_ticks=True,
2296
+ ):
2297
+ """Draw edge labels.
2298
+
2299
+ Parameters
2300
+ ----------
2301
+ G : graph
2302
+ A networkx graph
2303
+
2304
+ pos : dictionary
2305
+ A dictionary with nodes as keys and positions as values.
2306
+ Positions should be sequences of length 2.
2307
+
2308
+ edge_labels : dictionary (default=None)
2309
+ Edge labels in a dictionary of labels keyed by edge two-tuple.
2310
+ Only labels for the keys in the dictionary are drawn.
2311
+
2312
+ label_pos : float (default=0.5)
2313
+ Position of edge label along edge (0=head, 0.5=center, 1=tail)
2314
+
2315
+ font_size : int (default=10)
2316
+ Font size for text labels
2317
+
2318
+ font_color : color (default='k' black)
2319
+ Font color string. Color can be string or rgb (or rgba) tuple of
2320
+ floats from 0-1.
2321
+
2322
+ font_weight : string (default='normal')
2323
+ Font weight
2324
+
2325
+ font_family : string (default='sans-serif')
2326
+ Font family
2327
+
2328
+ alpha : float or None (default=None)
2329
+ The text transparency
2330
+
2331
+ bbox : Matplotlib bbox, optional
2332
+ Specify text box properties (e.g. shape, color etc.) for edge labels.
2333
+ Default is {boxstyle='round', ec=(1.0, 1.0, 1.0), fc=(1.0, 1.0, 1.0)}.
2334
+
2335
+ horizontalalignment : string (default='center')
2336
+ Horizontal alignment {'center', 'right', 'left'}
2337
+
2338
+ verticalalignment : string (default='center')
2339
+ Vertical alignment {'center', 'top', 'bottom', 'baseline', 'center_baseline'}
2340
+
2341
+ ax : Matplotlib Axes object, optional
2342
+ Draw the graph in the specified Matplotlib axes.
2343
+
2344
+ rotate : bool (default=True)
2345
+ Rotate edge labels to lie parallel to edges
2346
+
2347
+ clip_on : bool (default=True)
2348
+ Turn on clipping of edge labels at axis boundaries
2349
+
2350
+ node_size : scalar or array (default=300)
2351
+ Size of nodes. If an array it must be the same length as nodelist.
2352
+
2353
+ nodelist : list, optional (default=G.nodes())
2354
+ This provides the node order for the `node_size` array (if it is an array).
2355
+
2356
+ connectionstyle : string or iterable of strings (default="arc3")
2357
+ Pass the connectionstyle parameter to create curved arc of rounding
2358
+ radius rad. For example, connectionstyle='arc3,rad=0.2'.
2359
+ See `matplotlib.patches.ConnectionStyle` and
2360
+ `matplotlib.patches.FancyArrowPatch` for more info.
2361
+ If Iterable, index indicates i'th edge key of MultiGraph
2362
+
2363
+ hide_ticks : bool, optional
2364
+ Hide ticks of axes. When `True` (the default), ticks and ticklabels
2365
+ are removed from the axes. To set ticks and tick labels to the pyplot default,
2366
+ use ``hide_ticks=False``.
2367
+
2368
+ Returns
2369
+ -------
2370
+ dict
2371
+ `dict` of labels keyed by edge
2372
+
2373
+ Examples
2374
+ --------
2375
+ >>> G = nx.dodecahedral_graph()
2376
+ >>> edge_labels = nx.draw_networkx_edge_labels(G, pos=nx.spring_layout(G))
2377
+
2378
+ Also see the NetworkX drawing examples at
2379
+ https://networkx.org/documentation/latest/auto_examples/index.html
2380
+
2381
+ See Also
2382
+ --------
2383
+ draw
2384
+ draw_networkx
2385
+ draw_networkx_nodes
2386
+ draw_networkx_edges
2387
+ draw_networkx_labels
2388
+ """
2389
+ import matplotlib as mpl
2390
+ import matplotlib.pyplot as plt
2391
+ import numpy as np
2392
+
2393
+ class CurvedArrowText(CurvedArrowTextBase, mpl.text.Text):
2394
+ pass
2395
+
2396
+ # use default box of white with white border
2397
+ if bbox is None:
2398
+ bbox = {"boxstyle": "round", "ec": (1.0, 1.0, 1.0), "fc": (1.0, 1.0, 1.0)}
2399
+
2400
+ if isinstance(connectionstyle, str):
2401
+ connectionstyle = [connectionstyle]
2402
+ elif np.iterable(connectionstyle):
2403
+ connectionstyle = list(connectionstyle)
2404
+ else:
2405
+ raise nx.NetworkXError(
2406
+ "draw_networkx_edges arg `connectionstyle` must be"
2407
+ "string or iterable of strings"
2408
+ )
2409
+
2410
+ if ax is None:
2411
+ ax = plt.gca()
2412
+
2413
+ if edge_labels is None:
2414
+ kwds = {"keys": True} if G.is_multigraph() else {}
2415
+ edge_labels = {tuple(edge): d for *edge, d in G.edges(data=True, **kwds)}
2416
+ # NOTHING TO PLOT
2417
+ if not edge_labels:
2418
+ return {}
2419
+ edgelist, labels = zip(*edge_labels.items())
2420
+
2421
+ if nodelist is None:
2422
+ nodelist = list(G.nodes())
2423
+
2424
+ # set edge positions
2425
+ edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in edgelist])
2426
+
2427
+ if G.is_multigraph():
2428
+ key_count = collections.defaultdict(lambda: itertools.count(0))
2429
+ edge_indices = [next(key_count[tuple(e[:2])]) for e in edgelist]
2430
+ else:
2431
+ edge_indices = [0] * len(edgelist)
2432
+
2433
+ # Used to determine self loop mid-point
2434
+ # Note, that this will not be accurate,
2435
+ # if not drawing edge_labels for all edges drawn
2436
+ h = 0
2437
+ if edge_labels:
2438
+ miny = np.amin(np.ravel(edge_pos[:, :, 1]))
2439
+ maxy = np.amax(np.ravel(edge_pos[:, :, 1]))
2440
+ h = maxy - miny
2441
+ selfloop_height = h if h != 0 else 0.005 * np.array(node_size).max()
2442
+ fancy_arrow_factory = FancyArrowFactory(
2443
+ edge_pos,
2444
+ edgelist,
2445
+ nodelist,
2446
+ edge_indices,
2447
+ node_size,
2448
+ selfloop_height,
2449
+ connectionstyle,
2450
+ ax=ax,
2451
+ )
2452
+
2453
+ individual_params = {}
2454
+
2455
+ def check_individual_params(p_value, p_name):
2456
+ # TODO should this be list or array (as in a numpy array)?
2457
+ if isinstance(p_value, list):
2458
+ if len(p_value) != len(edgelist):
2459
+ raise ValueError(f"{p_name} must have the same length as edgelist.")
2460
+ individual_params[p_name] = p_value.iter()
2461
+
2462
+ # Don't need to pass in an edge because these are lists, not dicts
2463
+ def get_param_value(p_value, p_name):
2464
+ if p_name in individual_params:
2465
+ return next(individual_params[p_name])
2466
+ return p_value
2467
+
2468
+ check_individual_params(font_size, "font_size")
2469
+ check_individual_params(font_color, "font_color")
2470
+ check_individual_params(font_weight, "font_weight")
2471
+ check_individual_params(alpha, "alpha")
2472
+ check_individual_params(horizontalalignment, "horizontalalignment")
2473
+ check_individual_params(verticalalignment, "verticalalignment")
2474
+ check_individual_params(rotate, "rotate")
2475
+ check_individual_params(label_pos, "label_pos")
2476
+
2477
+ text_items = {}
2478
+ for i, (edge, label) in enumerate(zip(edgelist, labels)):
2479
+ if not isinstance(label, str):
2480
+ label = str(label) # this makes "1" and 1 labeled the same
2481
+
2482
+ n1, n2 = edge[:2]
2483
+ arrow = fancy_arrow_factory(i)
2484
+ if n1 == n2:
2485
+ connectionstyle_obj = arrow.get_connectionstyle()
2486
+ posA = ax.transData.transform(pos[n1])
2487
+ path_disp = connectionstyle_obj(posA, posA)
2488
+ path_data = ax.transData.inverted().transform_path(path_disp)
2489
+ x, y = path_data.vertices[0]
2490
+ text_items[edge] = ax.text(
2491
+ x,
2492
+ y,
2493
+ label,
2494
+ size=get_param_value(font_size, "font_size"),
2495
+ color=get_param_value(font_color, "font_color"),
2496
+ family=get_param_value(font_family, "font_family"),
2497
+ weight=get_param_value(font_weight, "font_weight"),
2498
+ alpha=get_param_value(alpha, "alpha"),
2499
+ horizontalalignment=get_param_value(
2500
+ horizontalalignment, "horizontalalignment"
2501
+ ),
2502
+ verticalalignment=get_param_value(
2503
+ verticalalignment, "verticalalignment"
2504
+ ),
2505
+ rotation=0,
2506
+ transform=ax.transData,
2507
+ bbox=bbox,
2508
+ zorder=1,
2509
+ clip_on=clip_on,
2510
+ )
2511
+ else:
2512
+ text_items[edge] = CurvedArrowText(
2513
+ arrow,
2514
+ label,
2515
+ size=get_param_value(font_size, "font_size"),
2516
+ color=get_param_value(font_color, "font_color"),
2517
+ family=get_param_value(font_family, "font_family"),
2518
+ weight=get_param_value(font_weight, "font_weight"),
2519
+ alpha=get_param_value(alpha, "alpha"),
2520
+ horizontalalignment=get_param_value(
2521
+ horizontalalignment, "horizontalalignment"
2522
+ ),
2523
+ verticalalignment=get_param_value(
2524
+ verticalalignment, "verticalalignment"
2525
+ ),
2526
+ transform=ax.transData,
2527
+ bbox=bbox,
2528
+ zorder=1,
2529
+ clip_on=clip_on,
2530
+ label_pos=get_param_value(label_pos, "label_pos"),
2531
+ labels_horizontal=not get_param_value(rotate, "rotate"),
2532
+ ax=ax,
2533
+ )
2534
+
2535
+ if hide_ticks:
2536
+ ax.tick_params(
2537
+ axis="both",
2538
+ which="both",
2539
+ bottom=False,
2540
+ left=False,
2541
+ labelbottom=False,
2542
+ labelleft=False,
2543
+ )
2544
+
2545
+ return text_items
2546
+
2547
+
2548
+ def draw_bipartite(G, **kwargs):
2549
+ """Draw the graph `G` with a bipartite layout.
2550
+
2551
+ This is a convenience function equivalent to::
2552
+
2553
+ nx.draw(G, pos=nx.bipartite_layout(G), **kwargs)
2554
+
2555
+ Parameters
2556
+ ----------
2557
+ G : graph
2558
+ A networkx graph
2559
+
2560
+ kwargs : optional keywords
2561
+ See `draw_networkx` for a description of optional keywords.
2562
+
2563
+ Raises
2564
+ ------
2565
+ NetworkXError :
2566
+ If `G` is not bipartite.
2567
+
2568
+ Notes
2569
+ -----
2570
+ The layout is computed each time this function is called. For
2571
+ repeated drawing it is much more efficient to call
2572
+ `~networkx.drawing.layout.bipartite_layout` directly and reuse the result::
2573
+
2574
+ >>> G = nx.complete_bipartite_graph(3, 3)
2575
+ >>> pos = nx.bipartite_layout(G)
2576
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2577
+ >>> # Draw a subgraph, reusing the same node positions
2578
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2579
+
2580
+ Examples
2581
+ --------
2582
+ >>> G = nx.complete_bipartite_graph(2, 5)
2583
+ >>> nx.draw_bipartite(G)
2584
+
2585
+ See Also
2586
+ --------
2587
+ :func:`~networkx.drawing.layout.bipartite_layout`
2588
+ """
2589
+ draw(G, pos=nx.bipartite_layout(G), **kwargs)
2590
+
2591
+
2592
+ def draw_circular(G, **kwargs):
2593
+ """Draw the graph `G` with a circular layout.
2594
+
2595
+ This is a convenience function equivalent to::
2596
+
2597
+ nx.draw(G, pos=nx.circular_layout(G), **kwargs)
2598
+
2599
+ Parameters
2600
+ ----------
2601
+ G : graph
2602
+ A networkx graph
2603
+
2604
+ kwargs : optional keywords
2605
+ See `draw_networkx` for a description of optional keywords.
2606
+
2607
+ Notes
2608
+ -----
2609
+ The layout is computed each time this function is called. For
2610
+ repeated drawing it is much more efficient to call
2611
+ `~networkx.drawing.layout.circular_layout` directly and reuse the result::
2612
+
2613
+ >>> G = nx.complete_graph(5)
2614
+ >>> pos = nx.circular_layout(G)
2615
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2616
+ >>> # Draw a subgraph, reusing the same node positions
2617
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2618
+
2619
+ Examples
2620
+ --------
2621
+ >>> G = nx.path_graph(5)
2622
+ >>> nx.draw_circular(G)
2623
+
2624
+ See Also
2625
+ --------
2626
+ :func:`~networkx.drawing.layout.circular_layout`
2627
+ """
2628
+ draw(G, pos=nx.circular_layout(G), **kwargs)
2629
+
2630
+
2631
+ def draw_kamada_kawai(G, **kwargs):
2632
+ """Draw the graph `G` with a Kamada-Kawai force-directed layout.
2633
+
2634
+ This is a convenience function equivalent to::
2635
+
2636
+ nx.draw(G, pos=nx.kamada_kawai_layout(G), **kwargs)
2637
+
2638
+ Parameters
2639
+ ----------
2640
+ G : graph
2641
+ A networkx graph
2642
+
2643
+ kwargs : optional keywords
2644
+ See `draw_networkx` for a description of optional keywords.
2645
+
2646
+ Notes
2647
+ -----
2648
+ The layout is computed each time this function is called.
2649
+ For repeated drawing it is much more efficient to call
2650
+ `~networkx.drawing.layout.kamada_kawai_layout` directly and reuse the
2651
+ result::
2652
+
2653
+ >>> G = nx.complete_graph(5)
2654
+ >>> pos = nx.kamada_kawai_layout(G)
2655
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2656
+ >>> # Draw a subgraph, reusing the same node positions
2657
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2658
+
2659
+ Examples
2660
+ --------
2661
+ >>> G = nx.path_graph(5)
2662
+ >>> nx.draw_kamada_kawai(G)
2663
+
2664
+ See Also
2665
+ --------
2666
+ :func:`~networkx.drawing.layout.kamada_kawai_layout`
2667
+ """
2668
+ draw(G, pos=nx.kamada_kawai_layout(G), **kwargs)
2669
+
2670
+
2671
+ def draw_random(G, **kwargs):
2672
+ """Draw the graph `G` with a random layout.
2673
+
2674
+ This is a convenience function equivalent to::
2675
+
2676
+ nx.draw(G, pos=nx.random_layout(G), **kwargs)
2677
+
2678
+ Parameters
2679
+ ----------
2680
+ G : graph
2681
+ A networkx graph
2682
+
2683
+ kwargs : optional keywords
2684
+ See `draw_networkx` for a description of optional keywords.
2685
+
2686
+ Notes
2687
+ -----
2688
+ The layout is computed each time this function is called.
2689
+ For repeated drawing it is much more efficient to call
2690
+ `~networkx.drawing.layout.random_layout` directly and reuse the result::
2691
+
2692
+ >>> G = nx.complete_graph(5)
2693
+ >>> pos = nx.random_layout(G)
2694
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2695
+ >>> # Draw a subgraph, reusing the same node positions
2696
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2697
+
2698
+ Examples
2699
+ --------
2700
+ >>> G = nx.lollipop_graph(4, 3)
2701
+ >>> nx.draw_random(G)
2702
+
2703
+ See Also
2704
+ --------
2705
+ :func:`~networkx.drawing.layout.random_layout`
2706
+ """
2707
+ draw(G, pos=nx.random_layout(G), **kwargs)
2708
+
2709
+
2710
+ def draw_spectral(G, **kwargs):
2711
+ """Draw the graph `G` with a spectral 2D layout.
2712
+
2713
+ This is a convenience function equivalent to::
2714
+
2715
+ nx.draw(G, pos=nx.spectral_layout(G), **kwargs)
2716
+
2717
+ For more information about how node positions are determined, see
2718
+ `~networkx.drawing.layout.spectral_layout`.
2719
+
2720
+ Parameters
2721
+ ----------
2722
+ G : graph
2723
+ A networkx graph
2724
+
2725
+ kwargs : optional keywords
2726
+ See `draw_networkx` for a description of optional keywords.
2727
+
2728
+ Notes
2729
+ -----
2730
+ The layout is computed each time this function is called.
2731
+ For repeated drawing it is much more efficient to call
2732
+ `~networkx.drawing.layout.spectral_layout` directly and reuse the result::
2733
+
2734
+ >>> G = nx.complete_graph(5)
2735
+ >>> pos = nx.spectral_layout(G)
2736
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2737
+ >>> # Draw a subgraph, reusing the same node positions
2738
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2739
+
2740
+ Examples
2741
+ --------
2742
+ >>> G = nx.path_graph(5)
2743
+ >>> nx.draw_spectral(G)
2744
+
2745
+ See Also
2746
+ --------
2747
+ :func:`~networkx.drawing.layout.spectral_layout`
2748
+ """
2749
+ draw(G, pos=nx.spectral_layout(G), **kwargs)
2750
+
2751
+
2752
+ def draw_spring(G, **kwargs):
2753
+ """Draw the graph `G` with a spring layout.
2754
+
2755
+ This is a convenience function equivalent to::
2756
+
2757
+ nx.draw(G, pos=nx.spring_layout(G), **kwargs)
2758
+
2759
+ Parameters
2760
+ ----------
2761
+ G : graph
2762
+ A networkx graph
2763
+
2764
+ kwargs : optional keywords
2765
+ See `draw_networkx` for a description of optional keywords.
2766
+
2767
+ Notes
2768
+ -----
2769
+ `~networkx.drawing.layout.spring_layout` is also the default layout for
2770
+ `draw`, so this function is equivalent to `draw`.
2771
+
2772
+ The layout is computed each time this function is called.
2773
+ For repeated drawing it is much more efficient to call
2774
+ `~networkx.drawing.layout.spring_layout` directly and reuse the result::
2775
+
2776
+ >>> G = nx.complete_graph(5)
2777
+ >>> pos = nx.spring_layout(G)
2778
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2779
+ >>> # Draw a subgraph, reusing the same node positions
2780
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2781
+
2782
+ Examples
2783
+ --------
2784
+ >>> G = nx.path_graph(20)
2785
+ >>> nx.draw_spring(G)
2786
+
2787
+ See Also
2788
+ --------
2789
+ draw
2790
+ :func:`~networkx.drawing.layout.spring_layout`
2791
+ """
2792
+ draw(G, pos=nx.spring_layout(G), **kwargs)
2793
+
2794
+
2795
+ def draw_shell(G, nlist=None, **kwargs):
2796
+ """Draw networkx graph `G` with shell layout.
2797
+
2798
+ This is a convenience function equivalent to::
2799
+
2800
+ nx.draw(G, pos=nx.shell_layout(G, nlist=nlist), **kwargs)
2801
+
2802
+ Parameters
2803
+ ----------
2804
+ G : graph
2805
+ A networkx graph
2806
+
2807
+ nlist : list of list of nodes, optional
2808
+ A list containing lists of nodes representing the shells.
2809
+ Default is `None`, meaning all nodes are in a single shell.
2810
+ See `~networkx.drawing.layout.shell_layout` for details.
2811
+
2812
+ kwargs : optional keywords
2813
+ See `draw_networkx` for a description of optional keywords.
2814
+
2815
+ Notes
2816
+ -----
2817
+ The layout is computed each time this function is called.
2818
+ For repeated drawing it is much more efficient to call
2819
+ `~networkx.drawing.layout.shell_layout` directly and reuse the result::
2820
+
2821
+ >>> G = nx.complete_graph(5)
2822
+ >>> pos = nx.shell_layout(G)
2823
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2824
+ >>> # Draw a subgraph, reusing the same node positions
2825
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2826
+
2827
+ Examples
2828
+ --------
2829
+ >>> G = nx.path_graph(4)
2830
+ >>> shells = [[0], [1, 2, 3]]
2831
+ >>> nx.draw_shell(G, nlist=shells)
2832
+
2833
+ See Also
2834
+ --------
2835
+ :func:`~networkx.drawing.layout.shell_layout`
2836
+ """
2837
+ draw(G, pos=nx.shell_layout(G, nlist=nlist), **kwargs)
2838
+
2839
+
2840
+ def draw_planar(G, **kwargs):
2841
+ """Draw a planar networkx graph `G` with planar layout.
2842
+
2843
+ This is a convenience function equivalent to::
2844
+
2845
+ nx.draw(G, pos=nx.planar_layout(G), **kwargs)
2846
+
2847
+ Parameters
2848
+ ----------
2849
+ G : graph
2850
+ A planar networkx graph
2851
+
2852
+ kwargs : optional keywords
2853
+ See `draw_networkx` for a description of optional keywords.
2854
+
2855
+ Raises
2856
+ ------
2857
+ NetworkXException
2858
+ When `G` is not planar
2859
+
2860
+ Notes
2861
+ -----
2862
+ The layout is computed each time this function is called.
2863
+ For repeated drawing it is much more efficient to call
2864
+ `~networkx.drawing.layout.planar_layout` directly and reuse the result::
2865
+
2866
+ >>> G = nx.path_graph(5)
2867
+ >>> pos = nx.planar_layout(G)
2868
+ >>> nx.draw(G, pos=pos) # Draw the original graph
2869
+ >>> # Draw a subgraph, reusing the same node positions
2870
+ >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
2871
+
2872
+ Examples
2873
+ --------
2874
+ >>> G = nx.path_graph(4)
2875
+ >>> nx.draw_planar(G)
2876
+
2877
+ See Also
2878
+ --------
2879
+ :func:`~networkx.drawing.layout.planar_layout`
2880
+ """
2881
+ draw(G, pos=nx.planar_layout(G), **kwargs)
2882
+
2883
+
2884
+ def draw_forceatlas2(G, **kwargs):
2885
+ """Draw a networkx graph with forceatlas2 layout.
2886
+
2887
+ This is a convenience function equivalent to::
2888
+
2889
+ nx.draw(G, pos=nx.forceatlas2_layout(G), **kwargs)
2890
+
2891
+ Parameters
2892
+ ----------
2893
+ G : graph
2894
+ A networkx graph
2895
+
2896
+ kwargs : optional keywords
2897
+ See networkx.draw_networkx() for a description of optional keywords,
2898
+ with the exception of the pos parameter which is not used by this
2899
+ function.
2900
+ """
2901
+ draw(G, pos=nx.forceatlas2_layout(G), **kwargs)
2902
+
2903
+
2904
+ def apply_alpha(colors, alpha, elem_list, cmap=None, vmin=None, vmax=None):
2905
+ """Apply an alpha (or list of alphas) to the colors provided.
2906
+
2907
+ Parameters
2908
+ ----------
2909
+
2910
+ colors : color string or array of floats (default='r')
2911
+ Color of element. Can be a single color format string,
2912
+ or a sequence of colors with the same length as nodelist.
2913
+ If numeric values are specified they will be mapped to
2914
+ colors using the cmap and vmin,vmax parameters. See
2915
+ matplotlib.scatter for more details.
2916
+
2917
+ alpha : float or array of floats
2918
+ Alpha values for elements. This can be a single alpha value, in
2919
+ which case it will be applied to all the elements of color. Otherwise,
2920
+ if it is an array, the elements of alpha will be applied to the colors
2921
+ in order (cycling through alpha multiple times if necessary).
2922
+
2923
+ elem_list : array of networkx objects
2924
+ The list of elements which are being colored. These could be nodes,
2925
+ edges or labels.
2926
+
2927
+ cmap : matplotlib colormap
2928
+ Color map for use if colors is a list of floats corresponding to points
2929
+ on a color mapping.
2930
+
2931
+ vmin, vmax : float
2932
+ Minimum and maximum values for normalizing colors if a colormap is used
2933
+
2934
+ Returns
2935
+ -------
2936
+
2937
+ rgba_colors : numpy ndarray
2938
+ Array containing RGBA format values for each of the node colours.
2939
+
2940
+ """
2941
+ from itertools import cycle, islice
2942
+
2943
+ import matplotlib as mpl
2944
+ import matplotlib.cm # call as mpl.cm
2945
+ import matplotlib.colors # call as mpl.colors
2946
+ import numpy as np
2947
+
2948
+ # If we have been provided with a list of numbers as long as elem_list,
2949
+ # apply the color mapping.
2950
+ if len(colors) == len(elem_list) and isinstance(colors[0], Number):
2951
+ mapper = mpl.cm.ScalarMappable(cmap=cmap)
2952
+ mapper.set_clim(vmin, vmax)
2953
+ rgba_colors = mapper.to_rgba(colors)
2954
+ # Otherwise, convert colors to matplotlib's RGB using the colorConverter
2955
+ # object. These are converted to numpy ndarrays to be consistent with the
2956
+ # to_rgba method of ScalarMappable.
2957
+ else:
2958
+ try:
2959
+ rgba_colors = np.array([mpl.colors.colorConverter.to_rgba(colors)])
2960
+ except ValueError:
2961
+ rgba_colors = np.array(
2962
+ [mpl.colors.colorConverter.to_rgba(color) for color in colors]
2963
+ )
2964
+ # Set the final column of the rgba_colors to have the relevant alpha values
2965
+ try:
2966
+ # If alpha is longer than the number of colors, resize to the number of
2967
+ # elements. Also, if rgba_colors.size (the number of elements of
2968
+ # rgba_colors) is the same as the number of elements, resize the array,
2969
+ # to avoid it being interpreted as a colormap by scatter()
2970
+ if len(alpha) > len(rgba_colors) or rgba_colors.size == len(elem_list):
2971
+ rgba_colors = np.resize(rgba_colors, (len(elem_list), 4))
2972
+ rgba_colors[1:, 0] = rgba_colors[0, 0]
2973
+ rgba_colors[1:, 1] = rgba_colors[0, 1]
2974
+ rgba_colors[1:, 2] = rgba_colors[0, 2]
2975
+ rgba_colors[:, 3] = list(islice(cycle(alpha), len(rgba_colors)))
2976
+ except TypeError:
2977
+ rgba_colors[:, -1] = alpha
2978
+ return rgba_colors