test_text.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498
  1. import random
  2. from itertools import product
  3. from textwrap import dedent
  4. import pytest
  5. import networkx as nx
  6. def test_forest_str_directed():
  7. # Create a directed forest with labels
  8. graph = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
  9. for node in graph.nodes:
  10. graph.nodes[node]["label"] = "node_" + chr(ord("a") + node)
  11. node_target = dedent(
  12. """
  13. ╙── 0
  14. ├─╼ 1
  15. │ ├─╼ 3
  16. │ └─╼ 4
  17. └─╼ 2
  18. ├─╼ 5
  19. └─╼ 6
  20. """
  21. ).strip()
  22. label_target = dedent(
  23. """
  24. ╙── node_a
  25. ├─╼ node_b
  26. │ ├─╼ node_d
  27. │ └─╼ node_e
  28. └─╼ node_c
  29. ├─╼ node_f
  30. └─╼ node_g
  31. """
  32. ).strip()
  33. # Basic node case
  34. ret = nx.forest_str(graph, with_labels=False)
  35. print(ret)
  36. assert ret == node_target
  37. # Basic label case
  38. ret = nx.forest_str(graph, with_labels=True)
  39. print(ret)
  40. assert ret == label_target
  41. # Custom write function case
  42. lines = []
  43. ret = nx.forest_str(graph, write=lines.append, with_labels=False)
  44. assert ret is None
  45. assert lines == node_target.split("\n")
  46. # Smoke test to ensure passing the print function works. To properly test
  47. # this case we would need to capture stdout. (for potential reference
  48. # implementation see :class:`ubelt.util_stream.CaptureStdout`)
  49. ret = nx.forest_str(graph, write=print)
  50. assert ret is None
  51. def test_write_network_text_empty_graph():
  52. def _graph_str(g, **kw):
  53. printbuf = []
  54. nx.write_network_text(g, printbuf.append, end="", **kw)
  55. return "\n".join(printbuf)
  56. assert _graph_str(nx.DiGraph()) == "╙"
  57. assert _graph_str(nx.Graph()) == "╙"
  58. assert _graph_str(nx.DiGraph(), ascii_only=True) == "+"
  59. assert _graph_str(nx.Graph(), ascii_only=True) == "+"
  60. def test_write_network_text_within_forest_glyph():
  61. g = nx.DiGraph()
  62. g.add_nodes_from([1, 2, 3, 4])
  63. g.add_edge(2, 4)
  64. lines = []
  65. write = lines.append
  66. nx.write_network_text(g, path=write, end="")
  67. nx.write_network_text(g, path=write, ascii_only=True, end="")
  68. text = "\n".join(lines)
  69. print(text)
  70. target = dedent(
  71. """
  72. ╟── 1
  73. ╟── 2
  74. ╎ └─╼ 4
  75. ╙── 3
  76. +-- 1
  77. +-- 2
  78. : L-> 4
  79. +-- 3
  80. """
  81. ).strip()
  82. assert text == target
  83. def test_forest_str_directed_multi_tree():
  84. tree1 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
  85. tree2 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
  86. forest = nx.disjoint_union_all([tree1, tree2])
  87. ret = nx.forest_str(forest)
  88. print(ret)
  89. target = dedent(
  90. """
  91. ╟── 0
  92. ╎ ├─╼ 1
  93. ╎ │ ├─╼ 3
  94. ╎ │ └─╼ 4
  95. ╎ └─╼ 2
  96. ╎ ├─╼ 5
  97. ╎ └─╼ 6
  98. ╙── 7
  99. ├─╼ 8
  100. │ ├─╼ 10
  101. │ └─╼ 11
  102. └─╼ 9
  103. ├─╼ 12
  104. └─╼ 13
  105. """
  106. ).strip()
  107. assert ret == target
  108. tree3 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
  109. forest = nx.disjoint_union_all([tree1, tree2, tree3])
  110. ret = nx.forest_str(forest, sources=[0, 14, 7])
  111. print(ret)
  112. target = dedent(
  113. """
  114. ╟── 0
  115. ╎ ├─╼ 1
  116. ╎ │ ├─╼ 3
  117. ╎ │ └─╼ 4
  118. ╎ └─╼ 2
  119. ╎ ├─╼ 5
  120. ╎ └─╼ 6
  121. ╟── 14
  122. ╎ ├─╼ 15
  123. ╎ │ ├─╼ 17
  124. ╎ │ └─╼ 18
  125. ╎ └─╼ 16
  126. ╎ ├─╼ 19
  127. ╎ └─╼ 20
  128. ╙── 7
  129. ├─╼ 8
  130. │ ├─╼ 10
  131. │ └─╼ 11
  132. └─╼ 9
  133. ├─╼ 12
  134. └─╼ 13
  135. """
  136. ).strip()
  137. assert ret == target
  138. ret = nx.forest_str(forest, sources=[0, 14, 7], ascii_only=True)
  139. print(ret)
  140. target = dedent(
  141. """
  142. +-- 0
  143. : |-> 1
  144. : | |-> 3
  145. : | L-> 4
  146. : L-> 2
  147. : |-> 5
  148. : L-> 6
  149. +-- 14
  150. : |-> 15
  151. : | |-> 17
  152. : | L-> 18
  153. : L-> 16
  154. : |-> 19
  155. : L-> 20
  156. +-- 7
  157. |-> 8
  158. | |-> 10
  159. | L-> 11
  160. L-> 9
  161. |-> 12
  162. L-> 13
  163. """
  164. ).strip()
  165. assert ret == target
  166. def test_forest_str_undirected_multi_tree():
  167. tree1 = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
  168. tree2 = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
  169. tree2 = nx.relabel_nodes(tree2, {n: n + len(tree1) for n in tree2.nodes})
  170. forest = nx.union(tree1, tree2)
  171. ret = nx.forest_str(forest, sources=[0, 7])
  172. print(ret)
  173. target = dedent(
  174. """
  175. ╟── 0
  176. ╎ ├── 1
  177. ╎ │ ├── 3
  178. ╎ │ └── 4
  179. ╎ └── 2
  180. ╎ ├── 5
  181. ╎ └── 6
  182. ╙── 7
  183. ├── 8
  184. │ ├── 10
  185. │ └── 11
  186. └── 9
  187. ├── 12
  188. └── 13
  189. """
  190. ).strip()
  191. assert ret == target
  192. ret = nx.forest_str(forest, sources=[0, 7], ascii_only=True)
  193. print(ret)
  194. target = dedent(
  195. """
  196. +-- 0
  197. : |-- 1
  198. : | |-- 3
  199. : | L-- 4
  200. : L-- 2
  201. : |-- 5
  202. : L-- 6
  203. +-- 7
  204. |-- 8
  205. | |-- 10
  206. | L-- 11
  207. L-- 9
  208. |-- 12
  209. L-- 13
  210. """
  211. ).strip()
  212. assert ret == target
  213. def test_forest_str_undirected():
  214. # Create a directed forest
  215. graph = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
  216. # arbitrary starting point
  217. nx.forest_str(graph)
  218. node_target0 = dedent(
  219. """
  220. ╙── 0
  221. ├── 1
  222. │ ├── 3
  223. │ └── 4
  224. └── 2
  225. ├── 5
  226. └── 6
  227. """
  228. ).strip()
  229. # defined starting point
  230. ret = nx.forest_str(graph, sources=[0])
  231. print(ret)
  232. assert ret == node_target0
  233. # defined starting point
  234. node_target2 = dedent(
  235. """
  236. ╙── 2
  237. ├── 0
  238. │ └── 1
  239. │ ├── 3
  240. │ └── 4
  241. ├── 5
  242. └── 6
  243. """
  244. ).strip()
  245. ret = nx.forest_str(graph, sources=[2])
  246. print(ret)
  247. assert ret == node_target2
  248. def test_forest_str_errors():
  249. ugraph = nx.complete_graph(3, create_using=nx.Graph)
  250. with pytest.raises(nx.NetworkXNotImplemented):
  251. nx.forest_str(ugraph)
  252. dgraph = nx.complete_graph(3, create_using=nx.DiGraph)
  253. with pytest.raises(nx.NetworkXNotImplemented):
  254. nx.forest_str(dgraph)
  255. def test_forest_str_overspecified_sources():
  256. """
  257. When sources are directly specified, we won't be able to determine when we
  258. are in the last component, so there will always be a trailing, leftmost
  259. pipe.
  260. """
  261. graph = nx.disjoint_union_all(
  262. [
  263. nx.balanced_tree(r=2, h=1, create_using=nx.DiGraph),
  264. nx.balanced_tree(r=1, h=2, create_using=nx.DiGraph),
  265. nx.balanced_tree(r=2, h=1, create_using=nx.DiGraph),
  266. ]
  267. )
  268. # defined starting point
  269. target1 = dedent(
  270. """
  271. ╟── 0
  272. ╎ ├─╼ 1
  273. ╎ └─╼ 2
  274. ╟── 3
  275. ╎ └─╼ 4
  276. ╎ └─╼ 5
  277. ╟── 6
  278. ╎ ├─╼ 7
  279. ╎ └─╼ 8
  280. """
  281. ).strip()
  282. target2 = dedent(
  283. """
  284. ╟── 0
  285. ╎ ├─╼ 1
  286. ╎ └─╼ 2
  287. ╟── 3
  288. ╎ └─╼ 4
  289. ╎ └─╼ 5
  290. ╙── 6
  291. ├─╼ 7
  292. └─╼ 8
  293. """
  294. ).strip()
  295. lines = []
  296. nx.forest_str(graph, write=lines.append, sources=graph.nodes)
  297. got1 = "\n".join(lines)
  298. print("got1: ")
  299. print(got1)
  300. lines = []
  301. nx.forest_str(graph, write=lines.append)
  302. got2 = "\n".join(lines)
  303. print("got2: ")
  304. print(got2)
  305. assert got1 == target1
  306. assert got2 == target2
  307. def test_write_network_text_iterative_add_directed_edges():
  308. """
  309. Walk through the cases going from a disconnected to fully connected graph
  310. """
  311. graph = nx.DiGraph()
  312. graph.add_nodes_from([1, 2, 3, 4])
  313. lines = []
  314. write = lines.append
  315. write("--- initial state ---")
  316. nx.write_network_text(graph, path=write, end="")
  317. for i, j in product(graph.nodes, graph.nodes):
  318. write(f"--- add_edge({i}, {j}) ---")
  319. graph.add_edge(i, j)
  320. nx.write_network_text(graph, path=write, end="")
  321. text = "\n".join(lines)
  322. print(text)
  323. # defined starting point
  324. target = dedent(
  325. """
  326. --- initial state ---
  327. ╟── 1
  328. ╟── 2
  329. ╟── 3
  330. ╙── 4
  331. --- add_edge(1, 1) ---
  332. ╟── 1 ╾ 1
  333. ╎ └─╼ ...
  334. ╟── 2
  335. ╟── 3
  336. ╙── 4
  337. --- add_edge(1, 2) ---
  338. ╟── 1 ╾ 1
  339. ╎ ├─╼ 2
  340. ╎ └─╼ ...
  341. ╟── 3
  342. ╙── 4
  343. --- add_edge(1, 3) ---
  344. ╟── 1 ╾ 1
  345. ╎ ├─╼ 2
  346. ╎ ├─╼ 3
  347. ╎ └─╼ ...
  348. ╙── 4
  349. --- add_edge(1, 4) ---
  350. ╙── 1 ╾ 1
  351. ├─╼ 2
  352. ├─╼ 3
  353. ├─╼ 4
  354. └─╼ ...
  355. --- add_edge(2, 1) ---
  356. ╙── 2 ╾ 1
  357. └─╼ 1 ╾ 1
  358. ├─╼ 3
  359. ├─╼ 4
  360. └─╼ ...
  361. --- add_edge(2, 2) ---
  362. ╙── 1 ╾ 1, 2
  363. ├─╼ 2 ╾ 2
  364. │ └─╼ ...
  365. ├─╼ 3
  366. ├─╼ 4
  367. └─╼ ...
  368. --- add_edge(2, 3) ---
  369. ╙── 1 ╾ 1, 2
  370. ├─╼ 2 ╾ 2
  371. │ ├─╼ 3 ╾ 1
  372. │ └─╼ ...
  373. ├─╼ 4
  374. └─╼ ...
  375. --- add_edge(2, 4) ---
  376. ╙── 1 ╾ 1, 2
  377. ├─╼ 2 ╾ 2
  378. │ ├─╼ 3 ╾ 1
  379. │ ├─╼ 4 ╾ 1
  380. │ └─╼ ...
  381. └─╼ ...
  382. --- add_edge(3, 1) ---
  383. ╙── 2 ╾ 1, 2
  384. ├─╼ 1 ╾ 1, 3
  385. │ ├─╼ 3 ╾ 2
  386. │ │ └─╼ ...
  387. │ ├─╼ 4 ╾ 2
  388. │ └─╼ ...
  389. └─╼ ...
  390. --- add_edge(3, 2) ---
  391. ╙── 3 ╾ 1, 2
  392. ├─╼ 1 ╾ 1, 2
  393. │ ├─╼ 2 ╾ 2, 3
  394. │ │ ├─╼ 4 ╾ 1
  395. │ │ └─╼ ...
  396. │ └─╼ ...
  397. └─╼ ...
  398. --- add_edge(3, 3) ---
  399. ╙── 1 ╾ 1, 2, 3
  400. ├─╼ 2 ╾ 2, 3
  401. │ ├─╼ 3 ╾ 1, 3
  402. │ │ └─╼ ...
  403. │ ├─╼ 4 ╾ 1
  404. │ └─╼ ...
  405. └─╼ ...
  406. --- add_edge(3, 4) ---
  407. ╙── 1 ╾ 1, 2, 3
  408. ├─╼ 2 ╾ 2, 3
  409. │ ├─╼ 3 ╾ 1, 3
  410. │ │ ├─╼ 4 ╾ 1, 2
  411. │ │ └─╼ ...
  412. │ └─╼ ...
  413. └─╼ ...
  414. --- add_edge(4, 1) ---
  415. ╙── 2 ╾ 1, 2, 3
  416. ├─╼ 1 ╾ 1, 3, 4
  417. │ ├─╼ 3 ╾ 2, 3
  418. │ │ ├─╼ 4 ╾ 1, 2
  419. │ │ │ └─╼ ...
  420. │ │ └─╼ ...
  421. │ └─╼ ...
  422. └─╼ ...
  423. --- add_edge(4, 2) ---
  424. ╙── 3 ╾ 1, 2, 3
  425. ├─╼ 1 ╾ 1, 2, 4
  426. │ ├─╼ 2 ╾ 2, 3, 4
  427. │ │ ├─╼ 4 ╾ 1, 3
  428. │ │ │ └─╼ ...
  429. │ │ └─╼ ...
  430. │ └─╼ ...
  431. └─╼ ...
  432. --- add_edge(4, 3) ---
  433. ╙── 4 ╾ 1, 2, 3
  434. ├─╼ 1 ╾ 1, 2, 3
  435. │ ├─╼ 2 ╾ 2, 3, 4
  436. │ │ ├─╼ 3 ╾ 1, 3, 4
  437. │ │ │ └─╼ ...
  438. │ │ └─╼ ...
  439. │ └─╼ ...
  440. └─╼ ...
  441. --- add_edge(4, 4) ---
  442. ╙── 1 ╾ 1, 2, 3, 4
  443. ├─╼ 2 ╾ 2, 3, 4
  444. │ ├─╼ 3 ╾ 1, 3, 4
  445. │ │ ├─╼ 4 ╾ 1, 2, 4
  446. │ │ │ └─╼ ...
  447. │ │ └─╼ ...
  448. │ └─╼ ...
  449. └─╼ ...
  450. """
  451. ).strip()
  452. assert target == text
  453. def test_write_network_text_iterative_add_undirected_edges():
  454. """
  455. Walk through the cases going from a disconnected to fully connected graph
  456. """
  457. graph = nx.Graph()
  458. graph.add_nodes_from([1, 2, 3, 4])
  459. lines = []
  460. write = lines.append
  461. write("--- initial state ---")
  462. nx.write_network_text(graph, path=write, end="")
  463. for i, j in product(graph.nodes, graph.nodes):
  464. if i == j:
  465. continue
  466. write(f"--- add_edge({i}, {j}) ---")
  467. graph.add_edge(i, j)
  468. nx.write_network_text(graph, path=write, end="")
  469. text = "\n".join(lines)
  470. print(text)
  471. target = dedent(
  472. """
  473. --- initial state ---
  474. ╟── 1
  475. ╟── 2
  476. ╟── 3
  477. ╙── 4
  478. --- add_edge(1, 2) ---
  479. ╟── 3
  480. ╟── 4
  481. ╙── 1
  482. └── 2
  483. --- add_edge(1, 3) ---
  484. ╟── 4
  485. ╙── 2
  486. └── 1
  487. └── 3
  488. --- add_edge(1, 4) ---
  489. ╙── 2
  490. └── 1
  491. ├── 3
  492. └── 4
  493. --- add_edge(2, 1) ---
  494. ╙── 2
  495. └── 1
  496. ├── 3
  497. └── 4
  498. --- add_edge(2, 3) ---
  499. ╙── 4
  500. └── 1
  501. ├── 2
  502. │ └── 3 ─ 1
  503. └── ...
  504. --- add_edge(2, 4) ---
  505. ╙── 3
  506. ├── 1
  507. │ ├── 2 ─ 3
  508. │ │ └── 4 ─ 1
  509. │ └── ...
  510. └── ...
  511. --- add_edge(3, 1) ---
  512. ╙── 3
  513. ├── 1
  514. │ ├── 2 ─ 3
  515. │ │ └── 4 ─ 1
  516. │ └── ...
  517. └── ...
  518. --- add_edge(3, 2) ---
  519. ╙── 3
  520. ├── 1
  521. │ ├── 2 ─ 3
  522. │ │ └── 4 ─ 1
  523. │ └── ...
  524. └── ...
  525. --- add_edge(3, 4) ---
  526. ╙── 1
  527. ├── 2
  528. │ ├── 3 ─ 1
  529. │ │ └── 4 ─ 1, 2
  530. │ └── ...
  531. └── ...
  532. --- add_edge(4, 1) ---
  533. ╙── 1
  534. ├── 2
  535. │ ├── 3 ─ 1
  536. │ │ └── 4 ─ 1, 2
  537. │ └── ...
  538. └── ...
  539. --- add_edge(4, 2) ---
  540. ╙── 1
  541. ├── 2
  542. │ ├── 3 ─ 1
  543. │ │ └── 4 ─ 1, 2
  544. │ └── ...
  545. └── ...
  546. --- add_edge(4, 3) ---
  547. ╙── 1
  548. ├── 2
  549. │ ├── 3 ─ 1
  550. │ │ └── 4 ─ 1, 2
  551. │ └── ...
  552. └── ...
  553. """
  554. ).strip()
  555. assert target == text
  556. def test_write_network_text_iterative_add_random_directed_edges():
  557. """
  558. Walk through the cases going from a disconnected to fully connected graph
  559. """
  560. rng = random.Random(724466096)
  561. graph = nx.DiGraph()
  562. graph.add_nodes_from([1, 2, 3, 4, 5])
  563. possible_edges = list(product(graph.nodes, graph.nodes))
  564. rng.shuffle(possible_edges)
  565. graph.add_edges_from(possible_edges[0:8])
  566. lines = []
  567. write = lines.append
  568. write("--- initial state ---")
  569. nx.write_network_text(graph, path=write, end="")
  570. for i, j in possible_edges[8:12]:
  571. write(f"--- add_edge({i}, {j}) ---")
  572. graph.add_edge(i, j)
  573. nx.write_network_text(graph, path=write, end="")
  574. text = "\n".join(lines)
  575. print(text)
  576. target = dedent(
  577. """
  578. --- initial state ---
  579. ╙── 3 ╾ 5
  580. └─╼ 2 ╾ 2
  581. ├─╼ 4 ╾ 4
  582. │ ├─╼ 5
  583. │ │ ├─╼ 1 ╾ 1
  584. │ │ │ └─╼ ...
  585. │ │ └─╼ ...
  586. │ └─╼ ...
  587. └─╼ ...
  588. --- add_edge(4, 1) ---
  589. ╙── 3 ╾ 5
  590. └─╼ 2 ╾ 2
  591. ├─╼ 4 ╾ 4
  592. │ ├─╼ 5
  593. │ │ ├─╼ 1 ╾ 1, 4
  594. │ │ │ └─╼ ...
  595. │ │ └─╼ ...
  596. │ └─╼ ...
  597. └─╼ ...
  598. --- add_edge(2, 1) ---
  599. ╙── 3 ╾ 5
  600. └─╼ 2 ╾ 2
  601. ├─╼ 4 ╾ 4
  602. │ ├─╼ 5
  603. │ │ ├─╼ 1 ╾ 1, 4, 2
  604. │ │ │ └─╼ ...
  605. │ │ └─╼ ...
  606. │ └─╼ ...
  607. └─╼ ...
  608. --- add_edge(5, 2) ---
  609. ╙── 3 ╾ 5
  610. └─╼ 2 ╾ 2, 5
  611. ├─╼ 4 ╾ 4
  612. │ ├─╼ 5
  613. │ │ ├─╼ 1 ╾ 1, 4, 2
  614. │ │ │ └─╼ ...
  615. │ │ └─╼ ...
  616. │ └─╼ ...
  617. └─╼ ...
  618. --- add_edge(1, 5) ---
  619. ╙── 3 ╾ 5
  620. └─╼ 2 ╾ 2, 5
  621. ├─╼ 4 ╾ 4
  622. │ ├─╼ 5 ╾ 1
  623. │ │ ├─╼ 1 ╾ 1, 4, 2
  624. │ │ │ └─╼ ...
  625. │ │ └─╼ ...
  626. │ └─╼ ...
  627. └─╼ ...
  628. """
  629. ).strip()
  630. assert target == text
  631. def test_write_network_text_nearly_forest():
  632. g = nx.DiGraph()
  633. g.add_edge(1, 2)
  634. g.add_edge(1, 5)
  635. g.add_edge(2, 3)
  636. g.add_edge(3, 4)
  637. g.add_edge(5, 6)
  638. g.add_edge(6, 7)
  639. g.add_edge(6, 8)
  640. orig = g.copy()
  641. g.add_edge(1, 8) # forward edge
  642. g.add_edge(4, 2) # back edge
  643. g.add_edge(6, 3) # cross edge
  644. lines = []
  645. write = lines.append
  646. write("--- directed case ---")
  647. nx.write_network_text(orig, path=write, end="")
  648. write("--- add (1, 8), (4, 2), (6, 3) ---")
  649. nx.write_network_text(g, path=write, end="")
  650. write("--- undirected case ---")
  651. nx.write_network_text(orig.to_undirected(), path=write, sources=[1], end="")
  652. write("--- add (1, 8), (4, 2), (6, 3) ---")
  653. nx.write_network_text(g.to_undirected(), path=write, sources=[1], end="")
  654. text = "\n".join(lines)
  655. print(text)
  656. target = dedent(
  657. """
  658. --- directed case ---
  659. ╙── 1
  660. ├─╼ 2
  661. │ └─╼ 3
  662. │ └─╼ 4
  663. └─╼ 5
  664. └─╼ 6
  665. ├─╼ 7
  666. └─╼ 8
  667. --- add (1, 8), (4, 2), (6, 3) ---
  668. ╙── 1
  669. ├─╼ 2 ╾ 4
  670. │ └─╼ 3 ╾ 6
  671. │ └─╼ 4
  672. │ └─╼ ...
  673. ├─╼ 5
  674. │ └─╼ 6
  675. │ ├─╼ 7
  676. │ ├─╼ 8 ╾ 1
  677. │ └─╼ ...
  678. └─╼ ...
  679. --- undirected case ---
  680. ╙── 1
  681. ├── 2
  682. │ └── 3
  683. │ └── 4
  684. └── 5
  685. └── 6
  686. ├── 7
  687. └── 8
  688. --- add (1, 8), (4, 2), (6, 3) ---
  689. ╙── 1
  690. ├── 2
  691. │ ├── 3
  692. │ │ ├── 4 ─ 2
  693. │ │ └── 6
  694. │ │ ├── 5 ─ 1
  695. │ │ ├── 7
  696. │ │ └── 8 ─ 1
  697. │ └── ...
  698. └── ...
  699. """
  700. ).strip()
  701. assert target == text
  702. def test_write_network_text_complete_graph_ascii_only():
  703. graph = nx.generators.complete_graph(5, create_using=nx.DiGraph)
  704. lines = []
  705. write = lines.append
  706. write("--- directed case ---")
  707. nx.write_network_text(graph, path=write, ascii_only=True, end="")
  708. write("--- undirected case ---")
  709. nx.write_network_text(graph.to_undirected(), path=write, ascii_only=True, end="")
  710. text = "\n".join(lines)
  711. print(text)
  712. target = dedent(
  713. """
  714. --- directed case ---
  715. +-- 0 <- 1, 2, 3, 4
  716. |-> 1 <- 2, 3, 4
  717. | |-> 2 <- 0, 3, 4
  718. | | |-> 3 <- 0, 1, 4
  719. | | | |-> 4 <- 0, 1, 2
  720. | | | | L-> ...
  721. | | | L-> ...
  722. | | L-> ...
  723. | L-> ...
  724. L-> ...
  725. --- undirected case ---
  726. +-- 0
  727. |-- 1
  728. | |-- 2 - 0
  729. | | |-- 3 - 0, 1
  730. | | | L-- 4 - 0, 1, 2
  731. | | L-- ...
  732. | L-- ...
  733. L-- ...
  734. """
  735. ).strip()
  736. assert target == text
  737. def test_write_network_text_with_labels():
  738. graph = nx.generators.complete_graph(5, create_using=nx.DiGraph)
  739. for n in graph.nodes:
  740. graph.nodes[n]["label"] = f"Node(n={n})"
  741. lines = []
  742. write = lines.append
  743. nx.write_network_text(graph, path=write, with_labels=True, ascii_only=False, end="")
  744. text = "\n".join(lines)
  745. print(text)
  746. # Non trees with labels can get somewhat out of hand with network text
  747. # because we need to immediately show every non-tree edge to the right
  748. target = dedent(
  749. """
  750. ╙── Node(n=0) ╾ Node(n=1), Node(n=2), Node(n=3), Node(n=4)
  751. ├─╼ Node(n=1) ╾ Node(n=2), Node(n=3), Node(n=4)
  752. │ ├─╼ Node(n=2) ╾ Node(n=0), Node(n=3), Node(n=4)
  753. │ │ ├─╼ Node(n=3) ╾ Node(n=0), Node(n=1), Node(n=4)
  754. │ │ │ ├─╼ Node(n=4) ╾ Node(n=0), Node(n=1), Node(n=2)
  755. │ │ │ │ └─╼ ...
  756. │ │ │ └─╼ ...
  757. │ │ └─╼ ...
  758. │ └─╼ ...
  759. └─╼ ...
  760. """
  761. ).strip()
  762. assert target == text
  763. def test_write_network_text_complete_graphs():
  764. lines = []
  765. write = lines.append
  766. for k in [0, 1, 2, 3, 4, 5]:
  767. g = nx.generators.complete_graph(k)
  768. write(f"--- undirected k={k} ---")
  769. nx.write_network_text(g, path=write, end="")
  770. for k in [0, 1, 2, 3, 4, 5]:
  771. g = nx.generators.complete_graph(k, nx.DiGraph)
  772. write(f"--- directed k={k} ---")
  773. nx.write_network_text(g, path=write, end="")
  774. text = "\n".join(lines)
  775. print(text)
  776. target = dedent(
  777. """
  778. --- undirected k=0 ---
  779. --- undirected k=1 ---
  780. ╙── 0
  781. --- undirected k=2 ---
  782. ╙── 0
  783. └── 1
  784. --- undirected k=3 ---
  785. ╙── 0
  786. ├── 1
  787. │ └── 2 ─ 0
  788. └── ...
  789. --- undirected k=4 ---
  790. ╙── 0
  791. ├── 1
  792. │ ├── 2 ─ 0
  793. │ │ └── 3 ─ 0, 1
  794. │ └── ...
  795. └── ...
  796. --- undirected k=5 ---
  797. ╙── 0
  798. ├── 1
  799. │ ├── 2 ─ 0
  800. │ │ ├── 3 ─ 0, 1
  801. │ │ │ └── 4 ─ 0, 1, 2
  802. │ │ └── ...
  803. │ └── ...
  804. └── ...
  805. --- directed k=0 ---
  806. --- directed k=1 ---
  807. ╙── 0
  808. --- directed k=2 ---
  809. ╙── 0 ╾ 1
  810. └─╼ 1
  811. └─╼ ...
  812. --- directed k=3 ---
  813. ╙── 0 ╾ 1, 2
  814. ├─╼ 1 ╾ 2
  815. │ ├─╼ 2 ╾ 0
  816. │ │ └─╼ ...
  817. │ └─╼ ...
  818. └─╼ ...
  819. --- directed k=4 ---
  820. ╙── 0 ╾ 1, 2, 3
  821. ├─╼ 1 ╾ 2, 3
  822. │ ├─╼ 2 ╾ 0, 3
  823. │ │ ├─╼ 3 ╾ 0, 1
  824. │ │ │ └─╼ ...
  825. │ │ └─╼ ...
  826. │ └─╼ ...
  827. └─╼ ...
  828. --- directed k=5 ---
  829. ╙── 0 ╾ 1, 2, 3, 4
  830. ├─╼ 1 ╾ 2, 3, 4
  831. │ ├─╼ 2 ╾ 0, 3, 4
  832. │ │ ├─╼ 3 ╾ 0, 1, 4
  833. │ │ │ ├─╼ 4 ╾ 0, 1, 2
  834. │ │ │ │ └─╼ ...
  835. │ │ │ └─╼ ...
  836. │ │ └─╼ ...
  837. │ └─╼ ...
  838. └─╼ ...
  839. """
  840. ).strip()
  841. assert target == text
  842. def test_write_network_text_multiple_sources():
  843. g = nx.DiGraph()
  844. g.add_edge(1, 2)
  845. g.add_edge(1, 3)
  846. g.add_edge(2, 4)
  847. g.add_edge(3, 5)
  848. g.add_edge(3, 6)
  849. g.add_edge(5, 4)
  850. g.add_edge(4, 1)
  851. g.add_edge(1, 5)
  852. lines = []
  853. write = lines.append
  854. # Use each node as the starting point to demonstrate how the representation
  855. # changes.
  856. nodes = sorted(g.nodes())
  857. for n in nodes:
  858. write(f"--- source node: {n} ---")
  859. nx.write_network_text(g, path=write, sources=[n], end="")
  860. text = "\n".join(lines)
  861. print(text)
  862. target = dedent(
  863. """
  864. --- source node: 1 ---
  865. ╙── 1 ╾ 4
  866. ├─╼ 2
  867. │ └─╼ 4 ╾ 5
  868. │ └─╼ ...
  869. ├─╼ 3
  870. │ ├─╼ 5 ╾ 1
  871. │ │ └─╼ ...
  872. │ └─╼ 6
  873. └─╼ ...
  874. --- source node: 2 ---
  875. ╙── 2 ╾ 1
  876. └─╼ 4 ╾ 5
  877. └─╼ 1
  878. ├─╼ 3
  879. │ ├─╼ 5 ╾ 1
  880. │ │ └─╼ ...
  881. │ └─╼ 6
  882. └─╼ ...
  883. --- source node: 3 ---
  884. ╙── 3 ╾ 1
  885. ├─╼ 5 ╾ 1
  886. │ └─╼ 4 ╾ 2
  887. │ └─╼ 1
  888. │ ├─╼ 2
  889. │ │ └─╼ ...
  890. │ └─╼ ...
  891. └─╼ 6
  892. --- source node: 4 ---
  893. ╙── 4 ╾ 2, 5
  894. └─╼ 1
  895. ├─╼ 2
  896. │ └─╼ ...
  897. ├─╼ 3
  898. │ ├─╼ 5 ╾ 1
  899. │ │ └─╼ ...
  900. │ └─╼ 6
  901. └─╼ ...
  902. --- source node: 5 ---
  903. ╙── 5 ╾ 3, 1
  904. └─╼ 4 ╾ 2
  905. └─╼ 1
  906. ├─╼ 2
  907. │ └─╼ ...
  908. ├─╼ 3
  909. │ ├─╼ 6
  910. │ └─╼ ...
  911. └─╼ ...
  912. --- source node: 6 ---
  913. ╙── 6 ╾ 3
  914. """
  915. ).strip()
  916. assert target == text
  917. def test_write_network_text_star_graph():
  918. graph = nx.star_graph(5, create_using=nx.Graph)
  919. lines = []
  920. write = lines.append
  921. nx.write_network_text(graph, path=write, end="")
  922. text = "\n".join(lines)
  923. print(text)
  924. target = dedent(
  925. """
  926. ╙── 1
  927. └── 0
  928. ├── 2
  929. ├── 3
  930. ├── 4
  931. └── 5
  932. """
  933. ).strip()
  934. assert target == text
  935. def test_write_network_text_path_graph():
  936. graph = nx.path_graph(3, create_using=nx.Graph)
  937. lines = []
  938. write = lines.append
  939. nx.write_network_text(graph, path=write, end="")
  940. text = "\n".join(lines)
  941. print(text)
  942. target = dedent(
  943. """
  944. ╙── 0
  945. └── 1
  946. └── 2
  947. """
  948. ).strip()
  949. assert target == text
  950. def test_write_network_text_lollipop_graph():
  951. graph = nx.lollipop_graph(4, 2, create_using=nx.Graph)
  952. lines = []
  953. write = lines.append
  954. nx.write_network_text(graph, path=write, end="")
  955. text = "\n".join(lines)
  956. print(text)
  957. target = dedent(
  958. """
  959. ╙── 5
  960. └── 4
  961. └── 3
  962. ├── 0
  963. │ ├── 1 ─ 3
  964. │ │ └── 2 ─ 0, 3
  965. │ └── ...
  966. └── ...
  967. """
  968. ).strip()
  969. assert target == text
  970. def test_write_network_text_wheel_graph():
  971. graph = nx.wheel_graph(7, create_using=nx.Graph)
  972. lines = []
  973. write = lines.append
  974. nx.write_network_text(graph, path=write, end="")
  975. text = "\n".join(lines)
  976. print(text)
  977. target = dedent(
  978. """
  979. ╙── 1
  980. ├── 0
  981. │ ├── 2 ─ 1
  982. │ │ └── 3 ─ 0
  983. │ │ └── 4 ─ 0
  984. │ │ └── 5 ─ 0
  985. │ │ └── 6 ─ 0, 1
  986. │ └── ...
  987. └── ...
  988. """
  989. ).strip()
  990. assert target == text
  991. def test_write_network_text_circular_ladder_graph():
  992. graph = nx.circular_ladder_graph(4, create_using=nx.Graph)
  993. lines = []
  994. write = lines.append
  995. nx.write_network_text(graph, path=write, end="")
  996. text = "\n".join(lines)
  997. print(text)
  998. target = dedent(
  999. """
  1000. ╙── 0
  1001. ├── 1
  1002. │ ├── 2
  1003. │ │ ├── 3 ─ 0
  1004. │ │ │ └── 7
  1005. │ │ │ ├── 6 ─ 2
  1006. │ │ │ │ └── 5 ─ 1
  1007. │ │ │ │ └── 4 ─ 0, 7
  1008. │ │ │ └── ...
  1009. │ │ └── ...
  1010. │ └── ...
  1011. └── ...
  1012. """
  1013. ).strip()
  1014. assert target == text
  1015. def test_write_network_text_dorogovtsev_goltsev_mendes_graph():
  1016. graph = nx.dorogovtsev_goltsev_mendes_graph(4, create_using=nx.Graph)
  1017. lines = []
  1018. write = lines.append
  1019. nx.write_network_text(graph, path=write, end="")
  1020. text = "\n".join(lines)
  1021. print(text)
  1022. target = dedent(
  1023. """
  1024. ╙── 15
  1025. ├── 0
  1026. │ ├── 1 ─ 15
  1027. │ │ ├── 2 ─ 0
  1028. │ │ │ ├── 4 ─ 0
  1029. │ │ │ │ ├── 9 ─ 0
  1030. │ │ │ │ │ ├── 22 ─ 0
  1031. │ │ │ │ │ └── 38 ─ 4
  1032. │ │ │ │ ├── 13 ─ 2
  1033. │ │ │ │ │ ├── 34 ─ 2
  1034. │ │ │ │ │ └── 39 ─ 4
  1035. │ │ │ │ ├── 18 ─ 0
  1036. │ │ │ │ ├── 30 ─ 2
  1037. │ │ │ │ └── ...
  1038. │ │ │ ├── 5 ─ 1
  1039. │ │ │ │ ├── 12 ─ 1
  1040. │ │ │ │ │ ├── 29 ─ 1
  1041. │ │ │ │ │ └── 40 ─ 5
  1042. │ │ │ │ ├── 14 ─ 2
  1043. │ │ │ │ │ ├── 35 ─ 2
  1044. │ │ │ │ │ └── 41 ─ 5
  1045. │ │ │ │ ├── 25 ─ 1
  1046. │ │ │ │ ├── 31 ─ 2
  1047. │ │ │ │ └── ...
  1048. │ │ │ ├── 7 ─ 0
  1049. │ │ │ │ ├── 20 ─ 0
  1050. │ │ │ │ └── 32 ─ 2
  1051. │ │ │ ├── 10 ─ 1
  1052. │ │ │ │ ├── 27 ─ 1
  1053. │ │ │ │ └── 33 ─ 2
  1054. │ │ │ ├── 16 ─ 0
  1055. │ │ │ ├── 23 ─ 1
  1056. │ │ │ └── ...
  1057. │ │ ├── 3 ─ 0
  1058. │ │ │ ├── 8 ─ 0
  1059. │ │ │ │ ├── 21 ─ 0
  1060. │ │ │ │ └── 36 ─ 3
  1061. │ │ │ ├── 11 ─ 1
  1062. │ │ │ │ ├── 28 ─ 1
  1063. │ │ │ │ └── 37 ─ 3
  1064. │ │ │ ├── 17 ─ 0
  1065. │ │ │ ├── 24 ─ 1
  1066. │ │ │ └── ...
  1067. │ │ ├── 6 ─ 0
  1068. │ │ │ ├── 19 ─ 0
  1069. │ │ │ └── 26 ─ 1
  1070. │ │ └── ...
  1071. │ └── ...
  1072. └── ...
  1073. """
  1074. ).strip()
  1075. assert target == text
  1076. def test_write_network_text_tree_max_depth():
  1077. orig = nx.balanced_tree(r=1, h=3, create_using=nx.DiGraph)
  1078. lines = []
  1079. write = lines.append
  1080. write("--- directed case, max_depth=0 ---")
  1081. nx.write_network_text(orig, path=write, end="", max_depth=0)
  1082. write("--- directed case, max_depth=1 ---")
  1083. nx.write_network_text(orig, path=write, end="", max_depth=1)
  1084. write("--- directed case, max_depth=2 ---")
  1085. nx.write_network_text(orig, path=write, end="", max_depth=2)
  1086. write("--- directed case, max_depth=3 ---")
  1087. nx.write_network_text(orig, path=write, end="", max_depth=3)
  1088. write("--- directed case, max_depth=4 ---")
  1089. nx.write_network_text(orig, path=write, end="", max_depth=4)
  1090. write("--- undirected case, max_depth=0 ---")
  1091. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
  1092. write("--- undirected case, max_depth=1 ---")
  1093. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
  1094. write("--- undirected case, max_depth=2 ---")
  1095. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
  1096. write("--- undirected case, max_depth=3 ---")
  1097. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
  1098. write("--- undirected case, max_depth=4 ---")
  1099. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=4)
  1100. text = "\n".join(lines)
  1101. print(text)
  1102. target = dedent(
  1103. """
  1104. --- directed case, max_depth=0 ---
  1105. ╙ ...
  1106. --- directed case, max_depth=1 ---
  1107. ╙── 0
  1108. └─╼ ...
  1109. --- directed case, max_depth=2 ---
  1110. ╙── 0
  1111. └─╼ 1
  1112. └─╼ ...
  1113. --- directed case, max_depth=3 ---
  1114. ╙── 0
  1115. └─╼ 1
  1116. └─╼ 2
  1117. └─╼ ...
  1118. --- directed case, max_depth=4 ---
  1119. ╙── 0
  1120. └─╼ 1
  1121. └─╼ 2
  1122. └─╼ 3
  1123. --- undirected case, max_depth=0 ---
  1124. ╙ ...
  1125. --- undirected case, max_depth=1 ---
  1126. ╙── 0 ─ 1
  1127. └── ...
  1128. --- undirected case, max_depth=2 ---
  1129. ╙── 0
  1130. └── 1 ─ 2
  1131. └── ...
  1132. --- undirected case, max_depth=3 ---
  1133. ╙── 0
  1134. └── 1
  1135. └── 2 ─ 3
  1136. └── ...
  1137. --- undirected case, max_depth=4 ---
  1138. ╙── 0
  1139. └── 1
  1140. └── 2
  1141. └── 3
  1142. """
  1143. ).strip()
  1144. assert target == text
  1145. def test_write_network_text_graph_max_depth():
  1146. orig = nx.erdos_renyi_graph(10, 0.15, directed=True, seed=40392)
  1147. lines = []
  1148. write = lines.append
  1149. write("--- directed case, max_depth=None ---")
  1150. nx.write_network_text(orig, path=write, end="", max_depth=None)
  1151. write("--- directed case, max_depth=0 ---")
  1152. nx.write_network_text(orig, path=write, end="", max_depth=0)
  1153. write("--- directed case, max_depth=1 ---")
  1154. nx.write_network_text(orig, path=write, end="", max_depth=1)
  1155. write("--- directed case, max_depth=2 ---")
  1156. nx.write_network_text(orig, path=write, end="", max_depth=2)
  1157. write("--- directed case, max_depth=3 ---")
  1158. nx.write_network_text(orig, path=write, end="", max_depth=3)
  1159. write("--- undirected case, max_depth=None ---")
  1160. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=None)
  1161. write("--- undirected case, max_depth=0 ---")
  1162. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
  1163. write("--- undirected case, max_depth=1 ---")
  1164. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
  1165. write("--- undirected case, max_depth=2 ---")
  1166. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
  1167. write("--- undirected case, max_depth=3 ---")
  1168. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
  1169. text = "\n".join(lines)
  1170. print(text)
  1171. target = dedent(
  1172. """
  1173. --- directed case, max_depth=None ---
  1174. ╟── 4
  1175. ╎ ├─╼ 0 ╾ 3
  1176. ╎ ├─╼ 5 ╾ 7
  1177. ╎ │ └─╼ 3
  1178. ╎ │ ├─╼ 1 ╾ 9
  1179. ╎ │ │ └─╼ 9 ╾ 6
  1180. ╎ │ │ ├─╼ 6
  1181. ╎ │ │ │ └─╼ ...
  1182. ╎ │ │ ├─╼ 7 ╾ 4
  1183. ╎ │ │ │ ├─╼ 2
  1184. ╎ │ │ │ └─╼ ...
  1185. ╎ │ │ └─╼ ...
  1186. ╎ │ └─╼ ...
  1187. ╎ └─╼ ...
  1188. ╙── 8
  1189. --- directed case, max_depth=0 ---
  1190. ╙ ...
  1191. --- directed case, max_depth=1 ---
  1192. ╟── 4
  1193. ╎ └─╼ ...
  1194. ╙── 8
  1195. --- directed case, max_depth=2 ---
  1196. ╟── 4
  1197. ╎ ├─╼ 0 ╾ 3
  1198. ╎ ├─╼ 5 ╾ 7
  1199. ╎ │ └─╼ ...
  1200. ╎ └─╼ 7 ╾ 9
  1201. ╎ └─╼ ...
  1202. ╙── 8
  1203. --- directed case, max_depth=3 ---
  1204. ╟── 4
  1205. ╎ ├─╼ 0 ╾ 3
  1206. ╎ ├─╼ 5 ╾ 7
  1207. ╎ │ └─╼ 3
  1208. ╎ │ └─╼ ...
  1209. ╎ └─╼ 7 ╾ 9
  1210. ╎ ├─╼ 2
  1211. ╎ └─╼ ...
  1212. ╙── 8
  1213. --- undirected case, max_depth=None ---
  1214. ╟── 8
  1215. ╙── 2
  1216. └── 7
  1217. ├── 4
  1218. │ ├── 0
  1219. │ │ └── 3
  1220. │ │ ├── 1
  1221. │ │ │ └── 9 ─ 7
  1222. │ │ │ └── 6
  1223. │ │ └── 5 ─ 4, 7
  1224. │ └── ...
  1225. └── ...
  1226. --- undirected case, max_depth=0 ---
  1227. ╙ ...
  1228. --- undirected case, max_depth=1 ---
  1229. ╟── 8
  1230. ╙── 2 ─ 7
  1231. └── ...
  1232. --- undirected case, max_depth=2 ---
  1233. ╟── 8
  1234. ╙── 2
  1235. └── 7 ─ 4, 5, 9
  1236. └── ...
  1237. --- undirected case, max_depth=3 ---
  1238. ╟── 8
  1239. ╙── 2
  1240. └── 7
  1241. ├── 4 ─ 0, 5
  1242. │ └── ...
  1243. ├── 5 ─ 4, 3
  1244. │ └── ...
  1245. └── 9 ─ 1, 6
  1246. └── ...
  1247. """
  1248. ).strip()
  1249. assert target == text
  1250. def test_write_network_text_clique_max_depth():
  1251. orig = nx.complete_graph(5, nx.DiGraph)
  1252. lines = []
  1253. write = lines.append
  1254. write("--- directed case, max_depth=None ---")
  1255. nx.write_network_text(orig, path=write, end="", max_depth=None)
  1256. write("--- directed case, max_depth=0 ---")
  1257. nx.write_network_text(orig, path=write, end="", max_depth=0)
  1258. write("--- directed case, max_depth=1 ---")
  1259. nx.write_network_text(orig, path=write, end="", max_depth=1)
  1260. write("--- directed case, max_depth=2 ---")
  1261. nx.write_network_text(orig, path=write, end="", max_depth=2)
  1262. write("--- directed case, max_depth=3 ---")
  1263. nx.write_network_text(orig, path=write, end="", max_depth=3)
  1264. write("--- undirected case, max_depth=None ---")
  1265. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=None)
  1266. write("--- undirected case, max_depth=0 ---")
  1267. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
  1268. write("--- undirected case, max_depth=1 ---")
  1269. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
  1270. write("--- undirected case, max_depth=2 ---")
  1271. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
  1272. write("--- undirected case, max_depth=3 ---")
  1273. nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
  1274. text = "\n".join(lines)
  1275. print(text)
  1276. target = dedent(
  1277. """
  1278. --- directed case, max_depth=None ---
  1279. ╙── 0 ╾ 1, 2, 3, 4
  1280. ├─╼ 1 ╾ 2, 3, 4
  1281. │ ├─╼ 2 ╾ 0, 3, 4
  1282. │ │ ├─╼ 3 ╾ 0, 1, 4
  1283. │ │ │ ├─╼ 4 ╾ 0, 1, 2
  1284. │ │ │ │ └─╼ ...
  1285. │ │ │ └─╼ ...
  1286. │ │ └─╼ ...
  1287. │ └─╼ ...
  1288. └─╼ ...
  1289. --- directed case, max_depth=0 ---
  1290. ╙ ...
  1291. --- directed case, max_depth=1 ---
  1292. ╙── 0 ╾ 1, 2, 3, 4
  1293. └─╼ ...
  1294. --- directed case, max_depth=2 ---
  1295. ╙── 0 ╾ 1, 2, 3, 4
  1296. ├─╼ 1 ╾ 2, 3, 4
  1297. │ └─╼ ...
  1298. ├─╼ 2 ╾ 1, 3, 4
  1299. │ └─╼ ...
  1300. ├─╼ 3 ╾ 1, 2, 4
  1301. │ └─╼ ...
  1302. └─╼ 4 ╾ 1, 2, 3
  1303. └─╼ ...
  1304. --- directed case, max_depth=3 ---
  1305. ╙── 0 ╾ 1, 2, 3, 4
  1306. ├─╼ 1 ╾ 2, 3, 4
  1307. │ ├─╼ 2 ╾ 0, 3, 4
  1308. │ │ └─╼ ...
  1309. │ ├─╼ 3 ╾ 0, 2, 4
  1310. │ │ └─╼ ...
  1311. │ ├─╼ 4 ╾ 0, 2, 3
  1312. │ │ └─╼ ...
  1313. │ └─╼ ...
  1314. └─╼ ...
  1315. --- undirected case, max_depth=None ---
  1316. ╙── 0
  1317. ├── 1
  1318. │ ├── 2 ─ 0
  1319. │ │ ├── 3 ─ 0, 1
  1320. │ │ │ └── 4 ─ 0, 1, 2
  1321. │ │ └── ...
  1322. │ └── ...
  1323. └── ...
  1324. --- undirected case, max_depth=0 ---
  1325. ╙ ...
  1326. --- undirected case, max_depth=1 ---
  1327. ╙── 0 ─ 1, 2, 3, 4
  1328. └── ...
  1329. --- undirected case, max_depth=2 ---
  1330. ╙── 0
  1331. ├── 1 ─ 2, 3, 4
  1332. │ └── ...
  1333. ├── 2 ─ 1, 3, 4
  1334. │ └── ...
  1335. ├── 3 ─ 1, 2, 4
  1336. │ └── ...
  1337. └── 4 ─ 1, 2, 3
  1338. --- undirected case, max_depth=3 ---
  1339. ╙── 0
  1340. ├── 1
  1341. │ ├── 2 ─ 0, 3, 4
  1342. │ │ └── ...
  1343. │ ├── 3 ─ 0, 2, 4
  1344. │ │ └── ...
  1345. │ └── 4 ─ 0, 2, 3
  1346. └── ...
  1347. """
  1348. ).strip()
  1349. assert target == text
  1350. def test_write_network_text_custom_label():
  1351. # Create a directed forest with labels
  1352. graph = nx.erdos_renyi_graph(5, 0.4, directed=True, seed=359222358)
  1353. for node in graph.nodes:
  1354. graph.nodes[node]["label"] = f"Node({node})"
  1355. graph.nodes[node]["chr"] = chr(node + ord("a") - 1)
  1356. if node % 2 == 0:
  1357. graph.nodes[node]["part"] = chr(node + ord("a"))
  1358. lines = []
  1359. write = lines.append
  1360. write("--- when with_labels=True, uses the 'label' attr ---")
  1361. nx.write_network_text(graph, path=write, with_labels=True, end="", max_depth=None)
  1362. write("--- when with_labels=False, uses str(node) value ---")
  1363. nx.write_network_text(graph, path=write, with_labels=False, end="", max_depth=None)
  1364. write("--- when with_labels is a string, use that attr ---")
  1365. nx.write_network_text(graph, path=write, with_labels="chr", end="", max_depth=None)
  1366. write("--- fallback to str(node) when the attr does not exist ---")
  1367. nx.write_network_text(graph, path=write, with_labels="part", end="", max_depth=None)
  1368. text = "\n".join(lines)
  1369. print(text)
  1370. target = dedent(
  1371. """
  1372. --- when with_labels=True, uses the 'label' attr ---
  1373. ╙── Node(1)
  1374. └─╼ Node(3) ╾ Node(2)
  1375. ├─╼ Node(0)
  1376. │ ├─╼ Node(2) ╾ Node(3), Node(4)
  1377. │ │ └─╼ ...
  1378. │ └─╼ Node(4)
  1379. │ └─╼ ...
  1380. └─╼ ...
  1381. --- when with_labels=False, uses str(node) value ---
  1382. ╙── 1
  1383. └─╼ 3 ╾ 2
  1384. ├─╼ 0
  1385. │ ├─╼ 2 ╾ 3, 4
  1386. │ │ └─╼ ...
  1387. │ └─╼ 4
  1388. │ └─╼ ...
  1389. └─╼ ...
  1390. --- when with_labels is a string, use that attr ---
  1391. ╙── a
  1392. └─╼ c ╾ b
  1393. ├─╼ `
  1394. │ ├─╼ b ╾ c, d
  1395. │ │ └─╼ ...
  1396. │ └─╼ d
  1397. │ └─╼ ...
  1398. └─╼ ...
  1399. --- fallback to str(node) when the attr does not exist ---
  1400. ╙── 1
  1401. └─╼ 3 ╾ c
  1402. ├─╼ a
  1403. │ ├─╼ c ╾ 3, e
  1404. │ │ └─╼ ...
  1405. │ └─╼ e
  1406. │ └─╼ ...
  1407. └─╼ ...
  1408. """
  1409. ).strip()
  1410. assert target == text