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,1463 @@
1
+ from collections import defaultdict
2
+ from copy import deepcopy
3
+
4
+ import networkx as nx
5
+
6
+ __all__ = ["check_planarity", "is_planar", "PlanarEmbedding"]
7
+
8
+
9
+ @nx._dispatchable
10
+ def is_planar(G):
11
+ """Returns True if and only if `G` is planar.
12
+
13
+ A graph is *planar* iff it can be drawn in a plane without
14
+ any edge intersections.
15
+
16
+ Parameters
17
+ ----------
18
+ G : NetworkX graph
19
+
20
+ Returns
21
+ -------
22
+ bool
23
+ Whether the graph is planar.
24
+
25
+ Examples
26
+ --------
27
+ >>> G = nx.Graph([(0, 1), (0, 2)])
28
+ >>> nx.is_planar(G)
29
+ True
30
+ >>> nx.is_planar(nx.complete_graph(5))
31
+ False
32
+
33
+ See Also
34
+ --------
35
+ check_planarity :
36
+ Check if graph is planar *and* return a `PlanarEmbedding` instance if True.
37
+ """
38
+
39
+ return check_planarity(G, counterexample=False)[0]
40
+
41
+
42
+ @nx._dispatchable(returns_graph=True)
43
+ def check_planarity(G, counterexample=False):
44
+ """Check if a graph is planar and return a counterexample or an embedding.
45
+
46
+ A graph is planar iff it can be drawn in a plane without
47
+ any edge intersections.
48
+
49
+ Parameters
50
+ ----------
51
+ G : NetworkX graph
52
+ counterexample : bool
53
+ A Kuratowski subgraph (to proof non planarity) is only returned if set
54
+ to true.
55
+
56
+ Returns
57
+ -------
58
+ (is_planar, certificate) : (bool, NetworkX graph) tuple
59
+ is_planar is true if the graph is planar.
60
+ If the graph is planar `certificate` is a PlanarEmbedding
61
+ otherwise it is a Kuratowski subgraph.
62
+
63
+ Examples
64
+ --------
65
+ >>> G = nx.Graph([(0, 1), (0, 2)])
66
+ >>> is_planar, P = nx.check_planarity(G)
67
+ >>> print(is_planar)
68
+ True
69
+
70
+ When `G` is planar, a `PlanarEmbedding` instance is returned:
71
+
72
+ >>> P.get_data()
73
+ {0: [1, 2], 1: [0], 2: [0]}
74
+
75
+ Notes
76
+ -----
77
+ A (combinatorial) embedding consists of cyclic orderings of the incident
78
+ edges at each vertex. Given such an embedding there are multiple approaches
79
+ discussed in literature to drawing the graph (subject to various
80
+ constraints, e.g. integer coordinates), see e.g. [2].
81
+
82
+ The planarity check algorithm and extraction of the combinatorial embedding
83
+ is based on the Left-Right Planarity Test [1].
84
+
85
+ A counterexample is only generated if the corresponding parameter is set,
86
+ because the complexity of the counterexample generation is higher.
87
+
88
+ See also
89
+ --------
90
+ is_planar :
91
+ Check for planarity without creating a `PlanarEmbedding` or counterexample.
92
+
93
+ References
94
+ ----------
95
+ .. [1] Ulrik Brandes:
96
+ The Left-Right Planarity Test
97
+ 2009
98
+ http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.217.9208
99
+ .. [2] Takao Nishizeki, Md Saidur Rahman:
100
+ Planar graph drawing
101
+ Lecture Notes Series on Computing: Volume 12
102
+ 2004
103
+ """
104
+
105
+ planarity_state = LRPlanarity(G)
106
+ embedding = planarity_state.lr_planarity()
107
+ if embedding is None:
108
+ # graph is not planar
109
+ if counterexample:
110
+ return False, get_counterexample(G)
111
+ else:
112
+ return False, None
113
+ else:
114
+ # graph is planar
115
+ return True, embedding
116
+
117
+
118
+ @nx._dispatchable(returns_graph=True)
119
+ def check_planarity_recursive(G, counterexample=False):
120
+ """Recursive version of :meth:`check_planarity`."""
121
+ planarity_state = LRPlanarity(G)
122
+ embedding = planarity_state.lr_planarity_recursive()
123
+ if embedding is None:
124
+ # graph is not planar
125
+ if counterexample:
126
+ return False, get_counterexample_recursive(G)
127
+ else:
128
+ return False, None
129
+ else:
130
+ # graph is planar
131
+ return True, embedding
132
+
133
+
134
+ @nx._dispatchable(returns_graph=True)
135
+ def get_counterexample(G):
136
+ """Obtains a Kuratowski subgraph.
137
+
138
+ Raises nx.NetworkXException if G is planar.
139
+
140
+ The function removes edges such that the graph is still not planar.
141
+ At some point the removal of any edge would make the graph planar.
142
+ This subgraph must be a Kuratowski subgraph.
143
+
144
+ Parameters
145
+ ----------
146
+ G : NetworkX graph
147
+
148
+ Returns
149
+ -------
150
+ subgraph : NetworkX graph
151
+ A Kuratowski subgraph that proves that G is not planar.
152
+
153
+ """
154
+ # copy graph
155
+ G = nx.Graph(G)
156
+
157
+ if check_planarity(G)[0]:
158
+ raise nx.NetworkXException("G is planar - no counter example.")
159
+
160
+ # find Kuratowski subgraph
161
+ subgraph = nx.Graph()
162
+ for u in G:
163
+ nbrs = list(G[u])
164
+ for v in nbrs:
165
+ G.remove_edge(u, v)
166
+ if check_planarity(G)[0]:
167
+ G.add_edge(u, v)
168
+ subgraph.add_edge(u, v)
169
+
170
+ return subgraph
171
+
172
+
173
+ @nx._dispatchable(returns_graph=True)
174
+ def get_counterexample_recursive(G):
175
+ """Recursive version of :meth:`get_counterexample`."""
176
+
177
+ # copy graph
178
+ G = nx.Graph(G)
179
+
180
+ if check_planarity_recursive(G)[0]:
181
+ raise nx.NetworkXException("G is planar - no counter example.")
182
+
183
+ # find Kuratowski subgraph
184
+ subgraph = nx.Graph()
185
+ for u in G:
186
+ nbrs = list(G[u])
187
+ for v in nbrs:
188
+ G.remove_edge(u, v)
189
+ if check_planarity_recursive(G)[0]:
190
+ G.add_edge(u, v)
191
+ subgraph.add_edge(u, v)
192
+
193
+ return subgraph
194
+
195
+
196
+ class Interval:
197
+ """Represents a set of return edges.
198
+
199
+ All return edges in an interval induce a same constraint on the contained
200
+ edges, which means that all edges must either have a left orientation or
201
+ all edges must have a right orientation.
202
+ """
203
+
204
+ def __init__(self, low=None, high=None):
205
+ self.low = low
206
+ self.high = high
207
+
208
+ def empty(self):
209
+ """Check if the interval is empty"""
210
+ return self.low is None and self.high is None
211
+
212
+ def copy(self):
213
+ """Returns a copy of this interval"""
214
+ return Interval(self.low, self.high)
215
+
216
+ def conflicting(self, b, planarity_state):
217
+ """Returns True if interval I conflicts with edge b"""
218
+ return (
219
+ not self.empty()
220
+ and planarity_state.lowpt[self.high] > planarity_state.lowpt[b]
221
+ )
222
+
223
+
224
+ class ConflictPair:
225
+ """Represents a different constraint between two intervals.
226
+
227
+ The edges in the left interval must have a different orientation than
228
+ the one in the right interval.
229
+ """
230
+
231
+ def __init__(self, left=Interval(), right=Interval()):
232
+ self.left = left
233
+ self.right = right
234
+
235
+ def swap(self):
236
+ """Swap left and right intervals"""
237
+ temp = self.left
238
+ self.left = self.right
239
+ self.right = temp
240
+
241
+ def lowest(self, planarity_state):
242
+ """Returns the lowest lowpoint of a conflict pair"""
243
+ if self.left.empty():
244
+ return planarity_state.lowpt[self.right.low]
245
+ if self.right.empty():
246
+ return planarity_state.lowpt[self.left.low]
247
+ return min(
248
+ planarity_state.lowpt[self.left.low], planarity_state.lowpt[self.right.low]
249
+ )
250
+
251
+
252
+ def top_of_stack(l):
253
+ """Returns the element on top of the stack."""
254
+ if not l:
255
+ return None
256
+ return l[-1]
257
+
258
+
259
+ class LRPlanarity:
260
+ """A class to maintain the state during planarity check."""
261
+
262
+ __slots__ = [
263
+ "G",
264
+ "roots",
265
+ "height",
266
+ "lowpt",
267
+ "lowpt2",
268
+ "nesting_depth",
269
+ "parent_edge",
270
+ "DG",
271
+ "adjs",
272
+ "ordered_adjs",
273
+ "ref",
274
+ "side",
275
+ "S",
276
+ "stack_bottom",
277
+ "lowpt_edge",
278
+ "left_ref",
279
+ "right_ref",
280
+ "embedding",
281
+ ]
282
+
283
+ def __init__(self, G):
284
+ # copy G without adding self-loops
285
+ self.G = nx.Graph()
286
+ self.G.add_nodes_from(G.nodes)
287
+ for e in G.edges:
288
+ if e[0] != e[1]:
289
+ self.G.add_edge(e[0], e[1])
290
+
291
+ self.roots = []
292
+
293
+ # distance from tree root
294
+ self.height = defaultdict(lambda: None)
295
+
296
+ self.lowpt = {} # height of lowest return point of an edge
297
+ self.lowpt2 = {} # height of second lowest return point
298
+ self.nesting_depth = {} # for nesting order
299
+
300
+ # None -> missing edge
301
+ self.parent_edge = defaultdict(lambda: None)
302
+
303
+ # oriented DFS graph
304
+ self.DG = nx.DiGraph()
305
+ self.DG.add_nodes_from(G.nodes)
306
+
307
+ self.adjs = {}
308
+ self.ordered_adjs = {}
309
+
310
+ self.ref = defaultdict(lambda: None)
311
+ self.side = defaultdict(lambda: 1)
312
+
313
+ # stack of conflict pairs
314
+ self.S = []
315
+ self.stack_bottom = {}
316
+ self.lowpt_edge = {}
317
+
318
+ self.left_ref = {}
319
+ self.right_ref = {}
320
+
321
+ self.embedding = PlanarEmbedding()
322
+
323
+ def lr_planarity(self):
324
+ """Execute the LR planarity test.
325
+
326
+ Returns
327
+ -------
328
+ embedding : dict
329
+ If the graph is planar an embedding is returned. Otherwise None.
330
+ """
331
+ if self.G.order() > 2 and self.G.size() > 3 * self.G.order() - 6:
332
+ # graph is not planar
333
+ return None
334
+
335
+ # make adjacency lists for dfs
336
+ for v in self.G:
337
+ self.adjs[v] = list(self.G[v])
338
+
339
+ # orientation of the graph by depth first search traversal
340
+ for v in self.G:
341
+ if self.height[v] is None:
342
+ self.height[v] = 0
343
+ self.roots.append(v)
344
+ self.dfs_orientation(v)
345
+
346
+ # Free no longer used variables
347
+ self.G = None
348
+ self.lowpt2 = None
349
+ self.adjs = None
350
+
351
+ # testing
352
+ for v in self.DG: # sort the adjacency lists by nesting depth
353
+ # note: this sorting leads to non linear time
354
+ self.ordered_adjs[v] = sorted(
355
+ self.DG[v], key=lambda x: self.nesting_depth[(v, x)]
356
+ )
357
+ for v in self.roots:
358
+ if not self.dfs_testing(v):
359
+ return None
360
+
361
+ # Free no longer used variables
362
+ self.height = None
363
+ self.lowpt = None
364
+ self.S = None
365
+ self.stack_bottom = None
366
+ self.lowpt_edge = None
367
+
368
+ for e in self.DG.edges:
369
+ self.nesting_depth[e] = self.sign(e) * self.nesting_depth[e]
370
+
371
+ self.embedding.add_nodes_from(self.DG.nodes)
372
+ for v in self.DG:
373
+ # sort the adjacency lists again
374
+ self.ordered_adjs[v] = sorted(
375
+ self.DG[v], key=lambda x: self.nesting_depth[(v, x)]
376
+ )
377
+ # initialize the embedding
378
+ previous_node = None
379
+ for w in self.ordered_adjs[v]:
380
+ self.embedding.add_half_edge(v, w, ccw=previous_node)
381
+ previous_node = w
382
+
383
+ # Free no longer used variables
384
+ self.DG = None
385
+ self.nesting_depth = None
386
+ self.ref = None
387
+
388
+ # compute the complete embedding
389
+ for v in self.roots:
390
+ self.dfs_embedding(v)
391
+
392
+ # Free no longer used variables
393
+ self.roots = None
394
+ self.parent_edge = None
395
+ self.ordered_adjs = None
396
+ self.left_ref = None
397
+ self.right_ref = None
398
+ self.side = None
399
+
400
+ return self.embedding
401
+
402
+ def lr_planarity_recursive(self):
403
+ """Recursive version of :meth:`lr_planarity`."""
404
+ if self.G.order() > 2 and self.G.size() > 3 * self.G.order() - 6:
405
+ # graph is not planar
406
+ return None
407
+
408
+ # orientation of the graph by depth first search traversal
409
+ for v in self.G:
410
+ if self.height[v] is None:
411
+ self.height[v] = 0
412
+ self.roots.append(v)
413
+ self.dfs_orientation_recursive(v)
414
+
415
+ # Free no longer used variable
416
+ self.G = None
417
+
418
+ # testing
419
+ for v in self.DG: # sort the adjacency lists by nesting depth
420
+ # note: this sorting leads to non linear time
421
+ self.ordered_adjs[v] = sorted(
422
+ self.DG[v], key=lambda x: self.nesting_depth[(v, x)]
423
+ )
424
+ for v in self.roots:
425
+ if not self.dfs_testing_recursive(v):
426
+ return None
427
+
428
+ for e in self.DG.edges:
429
+ self.nesting_depth[e] = self.sign_recursive(e) * self.nesting_depth[e]
430
+
431
+ self.embedding.add_nodes_from(self.DG.nodes)
432
+ for v in self.DG:
433
+ # sort the adjacency lists again
434
+ self.ordered_adjs[v] = sorted(
435
+ self.DG[v], key=lambda x: self.nesting_depth[(v, x)]
436
+ )
437
+ # initialize the embedding
438
+ previous_node = None
439
+ for w in self.ordered_adjs[v]:
440
+ self.embedding.add_half_edge(v, w, ccw=previous_node)
441
+ previous_node = w
442
+
443
+ # compute the complete embedding
444
+ for v in self.roots:
445
+ self.dfs_embedding_recursive(v)
446
+
447
+ return self.embedding
448
+
449
+ def dfs_orientation(self, v):
450
+ """Orient the graph by DFS, compute lowpoints and nesting order."""
451
+ # the recursion stack
452
+ dfs_stack = [v]
453
+ # index of next edge to handle in adjacency list of each node
454
+ ind = defaultdict(lambda: 0)
455
+ # boolean to indicate whether to skip the initial work for an edge
456
+ skip_init = defaultdict(lambda: False)
457
+
458
+ while dfs_stack:
459
+ v = dfs_stack.pop()
460
+ e = self.parent_edge[v]
461
+
462
+ for w in self.adjs[v][ind[v] :]:
463
+ vw = (v, w)
464
+
465
+ if not skip_init[vw]:
466
+ if (v, w) in self.DG.edges or (w, v) in self.DG.edges:
467
+ ind[v] += 1
468
+ continue # the edge was already oriented
469
+
470
+ self.DG.add_edge(v, w) # orient the edge
471
+
472
+ self.lowpt[vw] = self.height[v]
473
+ self.lowpt2[vw] = self.height[v]
474
+ if self.height[w] is None: # (v, w) is a tree edge
475
+ self.parent_edge[w] = vw
476
+ self.height[w] = self.height[v] + 1
477
+
478
+ dfs_stack.append(v) # revisit v after finishing w
479
+ dfs_stack.append(w) # visit w next
480
+ skip_init[vw] = True # don't redo this block
481
+ break # handle next node in dfs_stack (i.e. w)
482
+ else: # (v, w) is a back edge
483
+ self.lowpt[vw] = self.height[w]
484
+
485
+ # determine nesting graph
486
+ self.nesting_depth[vw] = 2 * self.lowpt[vw]
487
+ if self.lowpt2[vw] < self.height[v]: # chordal
488
+ self.nesting_depth[vw] += 1
489
+
490
+ # update lowpoints of parent edge e
491
+ if e is not None:
492
+ if self.lowpt[vw] < self.lowpt[e]:
493
+ self.lowpt2[e] = min(self.lowpt[e], self.lowpt2[vw])
494
+ self.lowpt[e] = self.lowpt[vw]
495
+ elif self.lowpt[vw] > self.lowpt[e]:
496
+ self.lowpt2[e] = min(self.lowpt2[e], self.lowpt[vw])
497
+ else:
498
+ self.lowpt2[e] = min(self.lowpt2[e], self.lowpt2[vw])
499
+
500
+ ind[v] += 1
501
+
502
+ def dfs_orientation_recursive(self, v):
503
+ """Recursive version of :meth:`dfs_orientation`."""
504
+ e = self.parent_edge[v]
505
+ for w in self.G[v]:
506
+ if (v, w) in self.DG.edges or (w, v) in self.DG.edges:
507
+ continue # the edge was already oriented
508
+ vw = (v, w)
509
+ self.DG.add_edge(v, w) # orient the edge
510
+
511
+ self.lowpt[vw] = self.height[v]
512
+ self.lowpt2[vw] = self.height[v]
513
+ if self.height[w] is None: # (v, w) is a tree edge
514
+ self.parent_edge[w] = vw
515
+ self.height[w] = self.height[v] + 1
516
+ self.dfs_orientation_recursive(w)
517
+ else: # (v, w) is a back edge
518
+ self.lowpt[vw] = self.height[w]
519
+
520
+ # determine nesting graph
521
+ self.nesting_depth[vw] = 2 * self.lowpt[vw]
522
+ if self.lowpt2[vw] < self.height[v]: # chordal
523
+ self.nesting_depth[vw] += 1
524
+
525
+ # update lowpoints of parent edge e
526
+ if e is not None:
527
+ if self.lowpt[vw] < self.lowpt[e]:
528
+ self.lowpt2[e] = min(self.lowpt[e], self.lowpt2[vw])
529
+ self.lowpt[e] = self.lowpt[vw]
530
+ elif self.lowpt[vw] > self.lowpt[e]:
531
+ self.lowpt2[e] = min(self.lowpt2[e], self.lowpt[vw])
532
+ else:
533
+ self.lowpt2[e] = min(self.lowpt2[e], self.lowpt2[vw])
534
+
535
+ def dfs_testing(self, v):
536
+ """Test for LR partition."""
537
+ # the recursion stack
538
+ dfs_stack = [v]
539
+ # index of next edge to handle in adjacency list of each node
540
+ ind = defaultdict(lambda: 0)
541
+ # boolean to indicate whether to skip the initial work for an edge
542
+ skip_init = defaultdict(lambda: False)
543
+
544
+ while dfs_stack:
545
+ v = dfs_stack.pop()
546
+ e = self.parent_edge[v]
547
+ # to indicate whether to skip the final block after the for loop
548
+ skip_final = False
549
+
550
+ for w in self.ordered_adjs[v][ind[v] :]:
551
+ ei = (v, w)
552
+
553
+ if not skip_init[ei]:
554
+ self.stack_bottom[ei] = top_of_stack(self.S)
555
+
556
+ if ei == self.parent_edge[w]: # tree edge
557
+ dfs_stack.append(v) # revisit v after finishing w
558
+ dfs_stack.append(w) # visit w next
559
+ skip_init[ei] = True # don't redo this block
560
+ skip_final = True # skip final work after breaking
561
+ break # handle next node in dfs_stack (i.e. w)
562
+ else: # back edge
563
+ self.lowpt_edge[ei] = ei
564
+ self.S.append(ConflictPair(right=Interval(ei, ei)))
565
+
566
+ # integrate new return edges
567
+ if self.lowpt[ei] < self.height[v]:
568
+ if w == self.ordered_adjs[v][0]: # e_i has return edge
569
+ self.lowpt_edge[e] = self.lowpt_edge[ei]
570
+ else: # add constraints of e_i
571
+ if not self.add_constraints(ei, e):
572
+ # graph is not planar
573
+ return False
574
+
575
+ ind[v] += 1
576
+
577
+ if not skip_final:
578
+ # remove back edges returning to parent
579
+ if e is not None: # v isn't root
580
+ self.remove_back_edges(e)
581
+
582
+ return True
583
+
584
+ def dfs_testing_recursive(self, v):
585
+ """Recursive version of :meth:`dfs_testing`."""
586
+ e = self.parent_edge[v]
587
+ for w in self.ordered_adjs[v]:
588
+ ei = (v, w)
589
+ self.stack_bottom[ei] = top_of_stack(self.S)
590
+ if ei == self.parent_edge[w]: # tree edge
591
+ if not self.dfs_testing_recursive(w):
592
+ return False
593
+ else: # back edge
594
+ self.lowpt_edge[ei] = ei
595
+ self.S.append(ConflictPair(right=Interval(ei, ei)))
596
+
597
+ # integrate new return edges
598
+ if self.lowpt[ei] < self.height[v]:
599
+ if w == self.ordered_adjs[v][0]: # e_i has return edge
600
+ self.lowpt_edge[e] = self.lowpt_edge[ei]
601
+ else: # add constraints of e_i
602
+ if not self.add_constraints(ei, e):
603
+ # graph is not planar
604
+ return False
605
+
606
+ # remove back edges returning to parent
607
+ if e is not None: # v isn't root
608
+ self.remove_back_edges(e)
609
+ return True
610
+
611
+ def add_constraints(self, ei, e):
612
+ P = ConflictPair()
613
+ # merge return edges of e_i into P.right
614
+ while True:
615
+ Q = self.S.pop()
616
+ if not Q.left.empty():
617
+ Q.swap()
618
+ if not Q.left.empty(): # not planar
619
+ return False
620
+ if self.lowpt[Q.right.low] > self.lowpt[e]:
621
+ # merge intervals
622
+ if P.right.empty(): # topmost interval
623
+ P.right = Q.right.copy()
624
+ else:
625
+ self.ref[P.right.low] = Q.right.high
626
+ P.right.low = Q.right.low
627
+ else: # align
628
+ self.ref[Q.right.low] = self.lowpt_edge[e]
629
+ if top_of_stack(self.S) == self.stack_bottom[ei]:
630
+ break
631
+ # merge conflicting return edges of e_1,...,e_i-1 into P.L
632
+ while top_of_stack(self.S).left.conflicting(ei, self) or top_of_stack(
633
+ self.S
634
+ ).right.conflicting(ei, self):
635
+ Q = self.S.pop()
636
+ if Q.right.conflicting(ei, self):
637
+ Q.swap()
638
+ if Q.right.conflicting(ei, self): # not planar
639
+ return False
640
+ # merge interval below lowpt(e_i) into P.R
641
+ self.ref[P.right.low] = Q.right.high
642
+ if Q.right.low is not None:
643
+ P.right.low = Q.right.low
644
+
645
+ if P.left.empty(): # topmost interval
646
+ P.left = Q.left.copy()
647
+ else:
648
+ self.ref[P.left.low] = Q.left.high
649
+ P.left.low = Q.left.low
650
+
651
+ if not (P.left.empty() and P.right.empty()):
652
+ self.S.append(P)
653
+ return True
654
+
655
+ def remove_back_edges(self, e):
656
+ u = e[0]
657
+ # trim back edges ending at parent u
658
+ # drop entire conflict pairs
659
+ while self.S and top_of_stack(self.S).lowest(self) == self.height[u]:
660
+ P = self.S.pop()
661
+ if P.left.low is not None:
662
+ self.side[P.left.low] = -1
663
+
664
+ if self.S: # one more conflict pair to consider
665
+ P = self.S.pop()
666
+ # trim left interval
667
+ while P.left.high is not None and P.left.high[1] == u:
668
+ P.left.high = self.ref[P.left.high]
669
+ if P.left.high is None and P.left.low is not None:
670
+ # just emptied
671
+ self.ref[P.left.low] = P.right.low
672
+ self.side[P.left.low] = -1
673
+ P.left.low = None
674
+ # trim right interval
675
+ while P.right.high is not None and P.right.high[1] == u:
676
+ P.right.high = self.ref[P.right.high]
677
+ if P.right.high is None and P.right.low is not None:
678
+ # just emptied
679
+ self.ref[P.right.low] = P.left.low
680
+ self.side[P.right.low] = -1
681
+ P.right.low = None
682
+ self.S.append(P)
683
+
684
+ # side of e is side of a highest return edge
685
+ if self.lowpt[e] < self.height[u]: # e has return edge
686
+ hl = top_of_stack(self.S).left.high
687
+ hr = top_of_stack(self.S).right.high
688
+
689
+ if hl is not None and (hr is None or self.lowpt[hl] > self.lowpt[hr]):
690
+ self.ref[e] = hl
691
+ else:
692
+ self.ref[e] = hr
693
+
694
+ def dfs_embedding(self, v):
695
+ """Completes the embedding."""
696
+ # the recursion stack
697
+ dfs_stack = [v]
698
+ # index of next edge to handle in adjacency list of each node
699
+ ind = defaultdict(lambda: 0)
700
+
701
+ while dfs_stack:
702
+ v = dfs_stack.pop()
703
+
704
+ for w in self.ordered_adjs[v][ind[v] :]:
705
+ ind[v] += 1
706
+ ei = (v, w)
707
+
708
+ if ei == self.parent_edge[w]: # tree edge
709
+ self.embedding.add_half_edge_first(w, v)
710
+ self.left_ref[v] = w
711
+ self.right_ref[v] = w
712
+
713
+ dfs_stack.append(v) # revisit v after finishing w
714
+ dfs_stack.append(w) # visit w next
715
+ break # handle next node in dfs_stack (i.e. w)
716
+ else: # back edge
717
+ if self.side[ei] == 1:
718
+ self.embedding.add_half_edge(w, v, ccw=self.right_ref[w])
719
+ else:
720
+ self.embedding.add_half_edge(w, v, cw=self.left_ref[w])
721
+ self.left_ref[w] = v
722
+
723
+ def dfs_embedding_recursive(self, v):
724
+ """Recursive version of :meth:`dfs_embedding`."""
725
+ for w in self.ordered_adjs[v]:
726
+ ei = (v, w)
727
+ if ei == self.parent_edge[w]: # tree edge
728
+ self.embedding.add_half_edge_first(w, v)
729
+ self.left_ref[v] = w
730
+ self.right_ref[v] = w
731
+ self.dfs_embedding_recursive(w)
732
+ else: # back edge
733
+ if self.side[ei] == 1:
734
+ # place v directly after right_ref[w] in embed. list of w
735
+ self.embedding.add_half_edge(w, v, ccw=self.right_ref[w])
736
+ else:
737
+ # place v directly before left_ref[w] in embed. list of w
738
+ self.embedding.add_half_edge(w, v, cw=self.left_ref[w])
739
+ self.left_ref[w] = v
740
+
741
+ def sign(self, e):
742
+ """Resolve the relative side of an edge to the absolute side."""
743
+ # the recursion stack
744
+ dfs_stack = [e]
745
+ # dict to remember reference edges
746
+ old_ref = defaultdict(lambda: None)
747
+
748
+ while dfs_stack:
749
+ e = dfs_stack.pop()
750
+
751
+ if self.ref[e] is not None:
752
+ dfs_stack.append(e) # revisit e after finishing self.ref[e]
753
+ dfs_stack.append(self.ref[e]) # visit self.ref[e] next
754
+ old_ref[e] = self.ref[e] # remember value of self.ref[e]
755
+ self.ref[e] = None
756
+ else:
757
+ self.side[e] *= self.side[old_ref[e]]
758
+
759
+ return self.side[e]
760
+
761
+ def sign_recursive(self, e):
762
+ """Recursive version of :meth:`sign`."""
763
+ if self.ref[e] is not None:
764
+ self.side[e] = self.side[e] * self.sign_recursive(self.ref[e])
765
+ self.ref[e] = None
766
+ return self.side[e]
767
+
768
+
769
+ class PlanarEmbedding(nx.DiGraph):
770
+ """Represents a planar graph with its planar embedding.
771
+
772
+ The planar embedding is given by a `combinatorial embedding
773
+ <https://en.wikipedia.org/wiki/Graph_embedding#Combinatorial_embedding>`_.
774
+
775
+ .. note:: `check_planarity` is the preferred way to check if a graph is planar.
776
+
777
+ **Neighbor ordering:**
778
+
779
+ In comparison to a usual graph structure, the embedding also stores the
780
+ order of all neighbors for every vertex.
781
+ The order of the neighbors can be given in clockwise (cw) direction or
782
+ counterclockwise (ccw) direction. This order is stored as edge attributes
783
+ in the underlying directed graph. For the edge (u, v) the edge attribute
784
+ 'cw' is set to the neighbor of u that follows immediately after v in
785
+ clockwise direction.
786
+
787
+ In order for a PlanarEmbedding to be valid it must fulfill multiple
788
+ conditions. It is possible to check if these conditions are fulfilled with
789
+ the method :meth:`check_structure`.
790
+ The conditions are:
791
+
792
+ * Edges must go in both directions (because the edge attributes differ)
793
+ * Every edge must have a 'cw' and 'ccw' attribute which corresponds to a
794
+ correct planar embedding.
795
+
796
+ As long as a PlanarEmbedding is invalid only the following methods should
797
+ be called:
798
+
799
+ * :meth:`add_half_edge`
800
+ * :meth:`connect_components`
801
+
802
+ Even though the graph is a subclass of nx.DiGraph, it can still be used
803
+ for algorithms that require undirected graphs, because the method
804
+ :meth:`is_directed` is overridden. This is possible, because a valid
805
+ PlanarGraph must have edges in both directions.
806
+
807
+ **Half edges:**
808
+
809
+ In methods like `add_half_edge` the term "half-edge" is used, which is
810
+ a term that is used in `doubly connected edge lists
811
+ <https://en.wikipedia.org/wiki/Doubly_connected_edge_list>`_. It is used
812
+ to emphasize that the edge is only in one direction and there exists
813
+ another half-edge in the opposite direction.
814
+ While conventional edges always have two faces (including outer face) next
815
+ to them, it is possible to assign each half-edge *exactly one* face.
816
+ For a half-edge (u, v) that is oriented such that u is below v then the
817
+ face that belongs to (u, v) is to the right of this half-edge.
818
+
819
+ See Also
820
+ --------
821
+ is_planar :
822
+ Preferred way to check if an existing graph is planar.
823
+
824
+ check_planarity :
825
+ A convenient way to create a `PlanarEmbedding`. If not planar,
826
+ it returns a subgraph that shows this.
827
+
828
+ Examples
829
+ --------
830
+
831
+ Create an embedding of a star graph (compare `nx.star_graph(3)`):
832
+
833
+ >>> G = nx.PlanarEmbedding()
834
+ >>> G.add_half_edge(0, 1)
835
+ >>> G.add_half_edge(0, 2, ccw=1)
836
+ >>> G.add_half_edge(0, 3, ccw=2)
837
+ >>> G.add_half_edge(1, 0)
838
+ >>> G.add_half_edge(2, 0)
839
+ >>> G.add_half_edge(3, 0)
840
+
841
+ Alternatively the same embedding can also be defined in counterclockwise
842
+ orientation. The following results in exactly the same PlanarEmbedding:
843
+
844
+ >>> G = nx.PlanarEmbedding()
845
+ >>> G.add_half_edge(0, 1)
846
+ >>> G.add_half_edge(0, 3, cw=1)
847
+ >>> G.add_half_edge(0, 2, cw=3)
848
+ >>> G.add_half_edge(1, 0)
849
+ >>> G.add_half_edge(2, 0)
850
+ >>> G.add_half_edge(3, 0)
851
+
852
+ After creating a graph, it is possible to validate that the PlanarEmbedding
853
+ object is correct:
854
+
855
+ >>> G.check_structure()
856
+
857
+ """
858
+
859
+ def __init__(self, incoming_graph_data=None, **attr):
860
+ super().__init__(incoming_graph_data=incoming_graph_data, **attr)
861
+ self.add_edge = self._forbidden
862
+ self.add_edges_from = self._forbidden
863
+ self.add_weighted_edges_from = self._forbidden
864
+
865
+ def _forbidden(self, *args, **kwargs):
866
+ """Forbidden operation
867
+
868
+ Any edge additions to a PlanarEmbedding should be done using
869
+ method `add_half_edge`.
870
+ """
871
+ raise NotImplementedError(
872
+ "Use `add_half_edge` method to add edges to a PlanarEmbedding."
873
+ )
874
+
875
+ def get_data(self):
876
+ """Converts the adjacency structure into a better readable structure.
877
+
878
+ Returns
879
+ -------
880
+ embedding : dict
881
+ A dict mapping all nodes to a list of neighbors sorted in
882
+ clockwise order.
883
+
884
+ See Also
885
+ --------
886
+ set_data
887
+
888
+ """
889
+ embedding = {}
890
+ for v in self:
891
+ embedding[v] = list(self.neighbors_cw_order(v))
892
+ return embedding
893
+
894
+ def set_data(self, data):
895
+ """Inserts edges according to given sorted neighbor list.
896
+
897
+ The input format is the same as the output format of get_data().
898
+
899
+ Parameters
900
+ ----------
901
+ data : dict
902
+ A dict mapping all nodes to a list of neighbors sorted in
903
+ clockwise order.
904
+
905
+ See Also
906
+ --------
907
+ get_data
908
+
909
+ """
910
+ for v in data:
911
+ ref = None
912
+ for w in reversed(data[v]):
913
+ self.add_half_edge(v, w, cw=ref)
914
+ ref = w
915
+
916
+ def remove_node(self, n):
917
+ """Remove node n.
918
+
919
+ Removes the node n and all adjacent edges, updating the
920
+ PlanarEmbedding to account for any resulting edge removal.
921
+ Attempting to remove a non-existent node will raise an exception.
922
+
923
+ Parameters
924
+ ----------
925
+ n : node
926
+ A node in the graph
927
+
928
+ Raises
929
+ ------
930
+ NetworkXError
931
+ If n is not in the graph.
932
+
933
+ See Also
934
+ --------
935
+ remove_nodes_from
936
+
937
+ """
938
+ try:
939
+ for u in self._pred[n]:
940
+ succs_u = self._succ[u]
941
+ un_cw = succs_u[n]["cw"]
942
+ un_ccw = succs_u[n]["ccw"]
943
+ del succs_u[n]
944
+ del self._pred[u][n]
945
+ if n != un_cw:
946
+ succs_u[un_cw]["ccw"] = un_ccw
947
+ succs_u[un_ccw]["cw"] = un_cw
948
+ del self._node[n]
949
+ del self._succ[n]
950
+ del self._pred[n]
951
+ except KeyError as err: # NetworkXError if n not in self
952
+ raise nx.NetworkXError(
953
+ f"The node {n} is not in the planar embedding."
954
+ ) from err
955
+ nx._clear_cache(self)
956
+
957
+ def remove_nodes_from(self, nodes):
958
+ """Remove multiple nodes.
959
+
960
+ Parameters
961
+ ----------
962
+ nodes : iterable container
963
+ A container of nodes (list, dict, set, etc.). If a node
964
+ in the container is not in the graph it is silently ignored.
965
+
966
+ See Also
967
+ --------
968
+ remove_node
969
+
970
+ Notes
971
+ -----
972
+ When removing nodes from an iterator over the graph you are changing,
973
+ a `RuntimeError` will be raised with message:
974
+ `RuntimeError: dictionary changed size during iteration`. This
975
+ happens when the graph's underlying dictionary is modified during
976
+ iteration. To avoid this error, evaluate the iterator into a separate
977
+ object, e.g. by using `list(iterator_of_nodes)`, and pass this
978
+ object to `G.remove_nodes_from`.
979
+
980
+ """
981
+ for n in nodes:
982
+ if n in self._node:
983
+ self.remove_node(n)
984
+ # silently skip non-existing nodes
985
+
986
+ def neighbors_cw_order(self, v):
987
+ """Generator for the neighbors of v in clockwise order.
988
+
989
+ Parameters
990
+ ----------
991
+ v : node
992
+
993
+ Yields
994
+ ------
995
+ node
996
+
997
+ """
998
+ succs = self._succ[v]
999
+ if not succs:
1000
+ # v has no neighbors
1001
+ return
1002
+ start_node = next(reversed(succs))
1003
+ yield start_node
1004
+ current_node = succs[start_node]["cw"]
1005
+ while start_node != current_node:
1006
+ yield current_node
1007
+ current_node = succs[current_node]["cw"]
1008
+
1009
+ def add_half_edge(self, start_node, end_node, *, cw=None, ccw=None):
1010
+ """Adds a half-edge from `start_node` to `end_node`.
1011
+
1012
+ If the half-edge is not the first one out of `start_node`, a reference
1013
+ node must be provided either in the clockwise (parameter `cw`) or in
1014
+ the counterclockwise (parameter `ccw`) direction. Only one of `cw`/`ccw`
1015
+ can be specified (or neither in the case of the first edge).
1016
+ Note that specifying a reference in the clockwise (`cw`) direction means
1017
+ inserting the new edge in the first counterclockwise position with
1018
+ respect to the reference (and vice-versa).
1019
+
1020
+ Parameters
1021
+ ----------
1022
+ start_node : node
1023
+ Start node of inserted edge.
1024
+ end_node : node
1025
+ End node of inserted edge.
1026
+ cw, ccw: node
1027
+ End node of reference edge.
1028
+ Omit or pass `None` if adding the first out-half-edge of `start_node`.
1029
+
1030
+
1031
+ Raises
1032
+ ------
1033
+ NetworkXException
1034
+ If the `cw` or `ccw` node is not a successor of `start_node`.
1035
+ If `start_node` has successors, but neither `cw` or `ccw` is provided.
1036
+ If both `cw` and `ccw` are specified.
1037
+
1038
+ See Also
1039
+ --------
1040
+ connect_components
1041
+ """
1042
+
1043
+ succs = self._succ.get(start_node)
1044
+ if succs:
1045
+ # there is already some edge out of start_node
1046
+ leftmost_nbr = next(reversed(self._succ[start_node]))
1047
+ if cw is not None:
1048
+ if cw not in succs:
1049
+ raise nx.NetworkXError("Invalid clockwise reference node.")
1050
+ if ccw is not None:
1051
+ raise nx.NetworkXError("Only one of cw/ccw can be specified.")
1052
+ ref_ccw = succs[cw]["ccw"]
1053
+ super().add_edge(start_node, end_node, cw=cw, ccw=ref_ccw)
1054
+ succs[ref_ccw]["cw"] = end_node
1055
+ succs[cw]["ccw"] = end_node
1056
+ # when (cw == leftmost_nbr), the newly added neighbor is
1057
+ # already at the end of dict self._succ[start_node] and
1058
+ # takes the place of the former leftmost_nbr
1059
+ move_leftmost_nbr_to_end = cw != leftmost_nbr
1060
+ elif ccw is not None:
1061
+ if ccw not in succs:
1062
+ raise nx.NetworkXError("Invalid counterclockwise reference node.")
1063
+ ref_cw = succs[ccw]["cw"]
1064
+ super().add_edge(start_node, end_node, cw=ref_cw, ccw=ccw)
1065
+ succs[ref_cw]["ccw"] = end_node
1066
+ succs[ccw]["cw"] = end_node
1067
+ move_leftmost_nbr_to_end = True
1068
+ else:
1069
+ raise nx.NetworkXError(
1070
+ "Node already has out-half-edge(s), either cw or ccw reference node required."
1071
+ )
1072
+ if move_leftmost_nbr_to_end:
1073
+ # LRPlanarity (via self.add_half_edge_first()) requires that
1074
+ # we keep track of the leftmost neighbor, which we accomplish
1075
+ # by keeping it as the last key in dict self._succ[start_node]
1076
+ succs[leftmost_nbr] = succs.pop(leftmost_nbr)
1077
+
1078
+ else:
1079
+ if cw is not None or ccw is not None:
1080
+ raise nx.NetworkXError("Invalid reference node.")
1081
+ # adding the first edge out of start_node
1082
+ super().add_edge(start_node, end_node, ccw=end_node, cw=end_node)
1083
+
1084
+ def check_structure(self):
1085
+ """Runs without exceptions if this object is valid.
1086
+
1087
+ Checks that the following properties are fulfilled:
1088
+
1089
+ * Edges go in both directions (because the edge attributes differ).
1090
+ * Every edge has a 'cw' and 'ccw' attribute which corresponds to a
1091
+ correct planar embedding.
1092
+
1093
+ Running this method verifies that the underlying Graph must be planar.
1094
+
1095
+ Raises
1096
+ ------
1097
+ NetworkXException
1098
+ This exception is raised with a short explanation if the
1099
+ PlanarEmbedding is invalid.
1100
+ """
1101
+ # Check fundamental structure
1102
+ for v in self:
1103
+ try:
1104
+ sorted_nbrs = set(self.neighbors_cw_order(v))
1105
+ except KeyError as err:
1106
+ msg = f"Bad embedding. Missing orientation for a neighbor of {v}"
1107
+ raise nx.NetworkXException(msg) from err
1108
+
1109
+ unsorted_nbrs = set(self[v])
1110
+ if sorted_nbrs != unsorted_nbrs:
1111
+ msg = "Bad embedding. Edge orientations not set correctly."
1112
+ raise nx.NetworkXException(msg)
1113
+ for w in self[v]:
1114
+ # Check if opposite half-edge exists
1115
+ if not self.has_edge(w, v):
1116
+ msg = "Bad embedding. Opposite half-edge is missing."
1117
+ raise nx.NetworkXException(msg)
1118
+
1119
+ # Check planarity
1120
+ counted_half_edges = set()
1121
+ for component in nx.connected_components(self):
1122
+ if len(component) == 1:
1123
+ # Don't need to check single node component
1124
+ continue
1125
+ num_nodes = len(component)
1126
+ num_half_edges = 0
1127
+ num_faces = 0
1128
+ for v in component:
1129
+ for w in self.neighbors_cw_order(v):
1130
+ num_half_edges += 1
1131
+ if (v, w) not in counted_half_edges:
1132
+ # We encountered a new face
1133
+ num_faces += 1
1134
+ # Mark all half-edges belonging to this face
1135
+ self.traverse_face(v, w, counted_half_edges)
1136
+ num_edges = num_half_edges // 2 # num_half_edges is even
1137
+ if num_nodes - num_edges + num_faces != 2:
1138
+ # The result does not match Euler's formula
1139
+ msg = "Bad embedding. The graph does not match Euler's formula"
1140
+ raise nx.NetworkXException(msg)
1141
+
1142
+ def add_half_edge_ccw(self, start_node, end_node, reference_neighbor):
1143
+ """Adds a half-edge from start_node to end_node.
1144
+
1145
+ The half-edge is added counter clockwise next to the existing half-edge
1146
+ (start_node, reference_neighbor).
1147
+
1148
+ Parameters
1149
+ ----------
1150
+ start_node : node
1151
+ Start node of inserted edge.
1152
+ end_node : node
1153
+ End node of inserted edge.
1154
+ reference_neighbor: node
1155
+ End node of reference edge.
1156
+
1157
+ Raises
1158
+ ------
1159
+ NetworkXException
1160
+ If the reference_neighbor does not exist.
1161
+
1162
+ See Also
1163
+ --------
1164
+ add_half_edge
1165
+ add_half_edge_cw
1166
+ connect_components
1167
+
1168
+ """
1169
+ self.add_half_edge(start_node, end_node, cw=reference_neighbor)
1170
+
1171
+ def add_half_edge_cw(self, start_node, end_node, reference_neighbor):
1172
+ """Adds a half-edge from start_node to end_node.
1173
+
1174
+ The half-edge is added clockwise next to the existing half-edge
1175
+ (start_node, reference_neighbor).
1176
+
1177
+ Parameters
1178
+ ----------
1179
+ start_node : node
1180
+ Start node of inserted edge.
1181
+ end_node : node
1182
+ End node of inserted edge.
1183
+ reference_neighbor: node
1184
+ End node of reference edge.
1185
+
1186
+ Raises
1187
+ ------
1188
+ NetworkXException
1189
+ If the reference_neighbor does not exist.
1190
+
1191
+ See Also
1192
+ --------
1193
+ add_half_edge
1194
+ add_half_edge_ccw
1195
+ connect_components
1196
+ """
1197
+ self.add_half_edge(start_node, end_node, ccw=reference_neighbor)
1198
+
1199
+ def remove_edge(self, u, v):
1200
+ """Remove the edge between u and v.
1201
+
1202
+ Parameters
1203
+ ----------
1204
+ u, v : nodes
1205
+ Remove the half-edges (u, v) and (v, u) and update the
1206
+ edge ordering around the removed edge.
1207
+
1208
+ Raises
1209
+ ------
1210
+ NetworkXError
1211
+ If there is not an edge between u and v.
1212
+
1213
+ See Also
1214
+ --------
1215
+ remove_edges_from : remove a collection of edges
1216
+ """
1217
+ try:
1218
+ succs_u = self._succ[u]
1219
+ succs_v = self._succ[v]
1220
+ uv_cw = succs_u[v]["cw"]
1221
+ uv_ccw = succs_u[v]["ccw"]
1222
+ vu_cw = succs_v[u]["cw"]
1223
+ vu_ccw = succs_v[u]["ccw"]
1224
+ del succs_u[v]
1225
+ del self._pred[v][u]
1226
+ del succs_v[u]
1227
+ del self._pred[u][v]
1228
+ if v != uv_cw:
1229
+ succs_u[uv_cw]["ccw"] = uv_ccw
1230
+ succs_u[uv_ccw]["cw"] = uv_cw
1231
+ if u != vu_cw:
1232
+ succs_v[vu_cw]["ccw"] = vu_ccw
1233
+ succs_v[vu_ccw]["cw"] = vu_cw
1234
+ except KeyError as err:
1235
+ raise nx.NetworkXError(
1236
+ f"The edge {u}-{v} is not in the planar embedding."
1237
+ ) from err
1238
+ nx._clear_cache(self)
1239
+
1240
+ def remove_edges_from(self, ebunch):
1241
+ """Remove all edges specified in ebunch.
1242
+
1243
+ Parameters
1244
+ ----------
1245
+ ebunch: list or container of edge tuples
1246
+ Each pair of half-edges between the nodes given in the tuples
1247
+ will be removed from the graph. The nodes can be passed as:
1248
+
1249
+ - 2-tuples (u, v) half-edges (u, v) and (v, u).
1250
+ - 3-tuples (u, v, k) where k is ignored.
1251
+
1252
+ See Also
1253
+ --------
1254
+ remove_edge : remove a single edge
1255
+
1256
+ Notes
1257
+ -----
1258
+ Will fail silently if an edge in ebunch is not in the graph.
1259
+
1260
+ Examples
1261
+ --------
1262
+ >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
1263
+ >>> ebunch = [(1, 2), (2, 3)]
1264
+ >>> G.remove_edges_from(ebunch)
1265
+ """
1266
+ for e in ebunch:
1267
+ u, v = e[:2] # ignore edge data
1268
+ # assuming that the PlanarEmbedding is valid, if the half_edge
1269
+ # (u, v) is in the graph, then so is half_edge (v, u)
1270
+ if u in self._succ and v in self._succ[u]:
1271
+ self.remove_edge(u, v)
1272
+
1273
+ def connect_components(self, v, w):
1274
+ """Adds half-edges for (v, w) and (w, v) at some position.
1275
+
1276
+ This method should only be called if v and w are in different
1277
+ components, or it might break the embedding.
1278
+ This especially means that if `connect_components(v, w)`
1279
+ is called it is not allowed to call `connect_components(w, v)`
1280
+ afterwards. The neighbor orientations in both directions are
1281
+ all set correctly after the first call.
1282
+
1283
+ Parameters
1284
+ ----------
1285
+ v : node
1286
+ w : node
1287
+
1288
+ See Also
1289
+ --------
1290
+ add_half_edge
1291
+ """
1292
+ if v in self._succ and self._succ[v]:
1293
+ ref = next(reversed(self._succ[v]))
1294
+ else:
1295
+ ref = None
1296
+ self.add_half_edge(v, w, cw=ref)
1297
+ if w in self._succ and self._succ[w]:
1298
+ ref = next(reversed(self._succ[w]))
1299
+ else:
1300
+ ref = None
1301
+ self.add_half_edge(w, v, cw=ref)
1302
+
1303
+ def add_half_edge_first(self, start_node, end_node):
1304
+ """Add a half-edge and set end_node as start_node's leftmost neighbor.
1305
+
1306
+ The new edge is inserted counterclockwise with respect to the current
1307
+ leftmost neighbor, if there is one.
1308
+
1309
+ Parameters
1310
+ ----------
1311
+ start_node : node
1312
+ end_node : node
1313
+
1314
+ See Also
1315
+ --------
1316
+ add_half_edge
1317
+ connect_components
1318
+ """
1319
+ succs = self._succ.get(start_node)
1320
+ # the leftmost neighbor is the last entry in the
1321
+ # self._succ[start_node] dict
1322
+ leftmost_nbr = next(reversed(succs)) if succs else None
1323
+ self.add_half_edge(start_node, end_node, cw=leftmost_nbr)
1324
+
1325
+ def next_face_half_edge(self, v, w):
1326
+ """Returns the following half-edge left of a face.
1327
+
1328
+ Parameters
1329
+ ----------
1330
+ v : node
1331
+ w : node
1332
+
1333
+ Returns
1334
+ -------
1335
+ half-edge : tuple
1336
+ """
1337
+ new_node = self[w][v]["ccw"]
1338
+ return w, new_node
1339
+
1340
+ def traverse_face(self, v, w, mark_half_edges=None):
1341
+ """Returns nodes on the face that belong to the half-edge (v, w).
1342
+
1343
+ The face that is traversed lies to the right of the half-edge (in an
1344
+ orientation where v is below w).
1345
+
1346
+ Optionally it is possible to pass a set to which all encountered half
1347
+ edges are added. Before calling this method, this set must not include
1348
+ any half-edges that belong to the face.
1349
+
1350
+ Parameters
1351
+ ----------
1352
+ v : node
1353
+ Start node of half-edge.
1354
+ w : node
1355
+ End node of half-edge.
1356
+ mark_half_edges: set, optional
1357
+ Set to which all encountered half-edges are added.
1358
+
1359
+ Returns
1360
+ -------
1361
+ face : list
1362
+ A list of nodes that lie on this face.
1363
+ """
1364
+ if mark_half_edges is None:
1365
+ mark_half_edges = set()
1366
+
1367
+ face_nodes = [v]
1368
+ mark_half_edges.add((v, w))
1369
+ prev_node = v
1370
+ cur_node = w
1371
+ # Last half-edge is (incoming_node, v)
1372
+ incoming_node = self[v][w]["cw"]
1373
+
1374
+ while cur_node != v or prev_node != incoming_node:
1375
+ face_nodes.append(cur_node)
1376
+ prev_node, cur_node = self.next_face_half_edge(prev_node, cur_node)
1377
+ if (prev_node, cur_node) in mark_half_edges:
1378
+ raise nx.NetworkXException("Bad planar embedding. Impossible face.")
1379
+ mark_half_edges.add((prev_node, cur_node))
1380
+
1381
+ return face_nodes
1382
+
1383
+ def is_directed(self):
1384
+ """A valid PlanarEmbedding is undirected.
1385
+
1386
+ All reverse edges are contained, i.e. for every existing
1387
+ half-edge (v, w) the half-edge in the opposite direction (w, v) is also
1388
+ contained.
1389
+ """
1390
+ return False
1391
+
1392
+ def copy(self, as_view=False):
1393
+ if as_view is True:
1394
+ return nx.graphviews.generic_graph_view(self)
1395
+ G = self.__class__()
1396
+ G.graph.update(self.graph)
1397
+ G.add_nodes_from((n, d.copy()) for n, d in self._node.items())
1398
+ super(self.__class__, G).add_edges_from(
1399
+ (u, v, datadict.copy())
1400
+ for u, nbrs in self._adj.items()
1401
+ for v, datadict in nbrs.items()
1402
+ )
1403
+ return G
1404
+
1405
+ def to_undirected(self, reciprocal=False, as_view=False):
1406
+ """
1407
+ Returns a non-embedding undirected representation of the graph.
1408
+
1409
+ This method strips the planar embedding information and provides
1410
+ a simple undirected graph representation. While creating the undirected graph,
1411
+ all edge attributes are retained except the ``"cw"`` and ``"ccw"`` attributes
1412
+ which are removed from the edge data. Those attributes are specific to
1413
+ the requirements of planar embeddings.
1414
+
1415
+ Parameters
1416
+ ----------
1417
+ reciprocal : bool (optional)
1418
+ Not supported for PlanarEmbedding. This parameter raises an exception
1419
+ if used. All valid embeddings include reciprocal half-edges by definition,
1420
+ making this parameter unnecessary.
1421
+ as_view : bool (optional, default=False)
1422
+ Not supported for PlanarEmbedding. This parameter raises an exception
1423
+ if used.
1424
+
1425
+ Returns
1426
+ -------
1427
+ G : Graph
1428
+ An undirected graph with the same name and nodes as the PlanarEmbedding.
1429
+ Edges are included with their data, except for the ``"cw"`` and ``"ccw"``
1430
+ attributes, which are omitted.
1431
+
1432
+
1433
+ Notes
1434
+ -----
1435
+ - If edges exist in both directions ``(u, v)`` and ``(v, u)`` in the PlanarEmbedding,
1436
+ attributes for the resulting undirected edge will be combined, excluding ``"cw"``
1437
+ and ``"ccw"``.
1438
+ - A deep copy is made of the other edge attributes as well as the
1439
+ node and graph attributes, ensuring independence of the resulting graph.
1440
+ - Subclass-specific data structures used in the original graph may not transfer
1441
+ to the undirected graph. The resulting graph will be of type ``nx.Graph``.
1442
+ """
1443
+
1444
+ if reciprocal:
1445
+ raise ValueError(
1446
+ "'reciprocal=True' is not supported for PlanarEmbedding.\n"
1447
+ "All valid embeddings include reciprocal half-edges by definition,\n"
1448
+ "making this parameter unnecessary."
1449
+ )
1450
+
1451
+ if as_view:
1452
+ raise ValueError("'as_view=True' is not supported for PlanarEmbedding.")
1453
+
1454
+ graph_class = self.to_undirected_class()
1455
+ G = graph_class()
1456
+ G.graph.update(deepcopy(self.graph))
1457
+ G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
1458
+ G.add_edges_from(
1459
+ (u, v, {k: deepcopy(v) for k, v in d.items() if k not in {"cw", "ccw"}})
1460
+ for u, nbrs in self._adj.items()
1461
+ for v, d in nbrs.items()
1462
+ )
1463
+ return G