Kano001's picture
Upload 1329 files
b200bda verified
import random
from itertools import product
from textwrap import dedent
import pytest
import networkx as nx
def test_forest_str_directed():
# Create a directed forest with labels
graph = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
for node in graph.nodes:
graph.nodes[node]["label"] = "node_" + chr(ord("a") + node)
node_target = dedent(
"""
╙── 0
β”œβ”€β•Ό 1
β”‚ β”œβ”€β•Ό 3
β”‚ └─╼ 4
└─╼ 2
β”œβ”€β•Ό 5
└─╼ 6
"""
).strip()
label_target = dedent(
"""
╙── node_a
β”œβ”€β•Ό node_b
β”‚ β”œβ”€β•Ό node_d
β”‚ └─╼ node_e
└─╼ node_c
β”œβ”€β•Ό node_f
└─╼ node_g
"""
).strip()
# Basic node case
ret = nx.forest_str(graph, with_labels=False)
print(ret)
assert ret == node_target
# Basic label case
ret = nx.forest_str(graph, with_labels=True)
print(ret)
assert ret == label_target
# Custom write function case
lines = []
ret = nx.forest_str(graph, write=lines.append, with_labels=False)
assert ret is None
assert lines == node_target.split("\n")
# Smoke test to ensure passing the print function works. To properly test
# this case we would need to capture stdout. (for potential reference
# implementation see :class:`ubelt.util_stream.CaptureStdout`)
ret = nx.forest_str(graph, write=print)
assert ret is None
def test_write_network_text_empty_graph():
def _graph_str(g, **kw):
printbuf = []
nx.write_network_text(g, printbuf.append, end="", **kw)
return "\n".join(printbuf)
assert _graph_str(nx.DiGraph()) == "β•™"
assert _graph_str(nx.Graph()) == "β•™"
assert _graph_str(nx.DiGraph(), ascii_only=True) == "+"
assert _graph_str(nx.Graph(), ascii_only=True) == "+"
def test_write_network_text_within_forest_glyph():
g = nx.DiGraph()
g.add_nodes_from([1, 2, 3, 4])
g.add_edge(2, 4)
lines = []
write = lines.append
nx.write_network_text(g, path=write, end="")
nx.write_network_text(g, path=write, ascii_only=True, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
β•Ÿβ”€β”€ 1
β•Ÿβ”€β”€ 2
β•Ž └─╼ 4
╙── 3
+-- 1
+-- 2
: L-> 4
+-- 3
"""
).strip()
assert text == target
def test_forest_str_directed_multi_tree():
tree1 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
tree2 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
forest = nx.disjoint_union_all([tree1, tree2])
ret = nx.forest_str(forest)
print(ret)
target = dedent(
"""
β•Ÿβ”€β”€ 0
β•Ž β”œβ”€β•Ό 1
β•Ž β”‚ β”œβ”€β•Ό 3
β•Ž β”‚ └─╼ 4
β•Ž └─╼ 2
β•Ž β”œβ”€β•Ό 5
β•Ž └─╼ 6
╙── 7
β”œβ”€β•Ό 8
β”‚ β”œβ”€β•Ό 10
β”‚ └─╼ 11
└─╼ 9
β”œβ”€β•Ό 12
└─╼ 13
"""
).strip()
assert ret == target
tree3 = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph)
forest = nx.disjoint_union_all([tree1, tree2, tree3])
ret = nx.forest_str(forest, sources=[0, 14, 7])
print(ret)
target = dedent(
"""
β•Ÿβ”€β”€ 0
β•Ž β”œβ”€β•Ό 1
β•Ž β”‚ β”œβ”€β•Ό 3
β•Ž β”‚ └─╼ 4
β•Ž └─╼ 2
β•Ž β”œβ”€β•Ό 5
β•Ž └─╼ 6
β•Ÿβ”€β”€ 14
β•Ž β”œβ”€β•Ό 15
β•Ž β”‚ β”œβ”€β•Ό 17
β•Ž β”‚ └─╼ 18
β•Ž └─╼ 16
β•Ž β”œβ”€β•Ό 19
β•Ž └─╼ 20
╙── 7
β”œβ”€β•Ό 8
β”‚ β”œβ”€β•Ό 10
β”‚ └─╼ 11
└─╼ 9
β”œβ”€β•Ό 12
└─╼ 13
"""
).strip()
assert ret == target
ret = nx.forest_str(forest, sources=[0, 14, 7], ascii_only=True)
print(ret)
target = dedent(
"""
+-- 0
: |-> 1
: | |-> 3
: | L-> 4
: L-> 2
: |-> 5
: L-> 6
+-- 14
: |-> 15
: | |-> 17
: | L-> 18
: L-> 16
: |-> 19
: L-> 20
+-- 7
|-> 8
| |-> 10
| L-> 11
L-> 9
|-> 12
L-> 13
"""
).strip()
assert ret == target
def test_forest_str_undirected_multi_tree():
tree1 = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
tree2 = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
tree2 = nx.relabel_nodes(tree2, {n: n + len(tree1) for n in tree2.nodes})
forest = nx.union(tree1, tree2)
ret = nx.forest_str(forest, sources=[0, 7])
print(ret)
target = dedent(
"""
β•Ÿβ”€β”€ 0
β•Ž β”œβ”€β”€ 1
β•Ž β”‚ β”œβ”€β”€ 3
β•Ž β”‚ └── 4
β•Ž └── 2
β•Ž β”œβ”€β”€ 5
β•Ž └── 6
╙── 7
β”œβ”€β”€ 8
β”‚ β”œβ”€β”€ 10
β”‚ └── 11
└── 9
β”œβ”€β”€ 12
└── 13
"""
).strip()
assert ret == target
ret = nx.forest_str(forest, sources=[0, 7], ascii_only=True)
print(ret)
target = dedent(
"""
+-- 0
: |-- 1
: | |-- 3
: | L-- 4
: L-- 2
: |-- 5
: L-- 6
+-- 7
|-- 8
| |-- 10
| L-- 11
L-- 9
|-- 12
L-- 13
"""
).strip()
assert ret == target
def test_forest_str_undirected():
# Create a directed forest
graph = nx.balanced_tree(r=2, h=2, create_using=nx.Graph)
# arbitrary starting point
nx.forest_str(graph)
node_target0 = dedent(
"""
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 3
β”‚ └── 4
└── 2
β”œβ”€β”€ 5
└── 6
"""
).strip()
# defined starting point
ret = nx.forest_str(graph, sources=[0])
print(ret)
assert ret == node_target0
# defined starting point
node_target2 = dedent(
"""
╙── 2
β”œβ”€β”€ 0
β”‚ └── 1
β”‚ β”œβ”€β”€ 3
β”‚ └── 4
β”œβ”€β”€ 5
└── 6
"""
).strip()
ret = nx.forest_str(graph, sources=[2])
print(ret)
assert ret == node_target2
def test_forest_str_errors():
ugraph = nx.complete_graph(3, create_using=nx.Graph)
with pytest.raises(nx.NetworkXNotImplemented):
nx.forest_str(ugraph)
dgraph = nx.complete_graph(3, create_using=nx.DiGraph)
with pytest.raises(nx.NetworkXNotImplemented):
nx.forest_str(dgraph)
def test_forest_str_overspecified_sources():
"""
When sources are directly specified, we won't be able to determine when we
are in the last component, so there will always be a trailing, leftmost
pipe.
"""
graph = nx.disjoint_union_all(
[
nx.balanced_tree(r=2, h=1, create_using=nx.DiGraph),
nx.balanced_tree(r=1, h=2, create_using=nx.DiGraph),
nx.balanced_tree(r=2, h=1, create_using=nx.DiGraph),
]
)
# defined starting point
target1 = dedent(
"""
β•Ÿβ”€β”€ 0
β•Ž β”œβ”€β•Ό 1
β•Ž └─╼ 2
β•Ÿβ”€β”€ 3
β•Ž └─╼ 4
β•Ž └─╼ 5
β•Ÿβ”€β”€ 6
β•Ž β”œβ”€β•Ό 7
β•Ž └─╼ 8
"""
).strip()
target2 = dedent(
"""
β•Ÿβ”€β”€ 0
β•Ž β”œβ”€β•Ό 1
β•Ž └─╼ 2
β•Ÿβ”€β”€ 3
β•Ž └─╼ 4
β•Ž └─╼ 5
╙── 6
β”œβ”€β•Ό 7
└─╼ 8
"""
).strip()
lines = []
nx.forest_str(graph, write=lines.append, sources=graph.nodes)
got1 = "\n".join(lines)
print("got1: ")
print(got1)
lines = []
nx.forest_str(graph, write=lines.append)
got2 = "\n".join(lines)
print("got2: ")
print(got2)
assert got1 == target1
assert got2 == target2
def test_write_network_text_iterative_add_directed_edges():
"""
Walk through the cases going from a disconnected to fully connected graph
"""
graph = nx.DiGraph()
graph.add_nodes_from([1, 2, 3, 4])
lines = []
write = lines.append
write("--- initial state ---")
nx.write_network_text(graph, path=write, end="")
for i, j in product(graph.nodes, graph.nodes):
write(f"--- add_edge({i}, {j}) ---")
graph.add_edge(i, j)
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
# defined starting point
target = dedent(
"""
--- initial state ---
β•Ÿβ”€β”€ 1
β•Ÿβ”€β”€ 2
β•Ÿβ”€β”€ 3
╙── 4
--- add_edge(1, 1) ---
β•Ÿβ”€β”€ 1 β•Ύ 1
β•Ž └─╼ ...
β•Ÿβ”€β”€ 2
β•Ÿβ”€β”€ 3
╙── 4
--- add_edge(1, 2) ---
β•Ÿβ”€β”€ 1 β•Ύ 1
β•Ž β”œβ”€β•Ό 2
β•Ž └─╼ ...
β•Ÿβ”€β”€ 3
╙── 4
--- add_edge(1, 3) ---
β•Ÿβ”€β”€ 1 β•Ύ 1
β•Ž β”œβ”€β•Ό 2
β•Ž β”œβ”€β•Ό 3
β•Ž └─╼ ...
╙── 4
--- add_edge(1, 4) ---
╙── 1 β•Ύ 1
β”œβ”€β•Ό 2
β”œβ”€β•Ό 3
β”œβ”€β•Ό 4
└─╼ ...
--- add_edge(2, 1) ---
╙── 2 β•Ύ 1
└─╼ 1 β•Ύ 1
β”œβ”€β•Ό 3
β”œβ”€β•Ό 4
└─╼ ...
--- add_edge(2, 2) ---
╙── 1 β•Ύ 1, 2
β”œβ”€β•Ό 2 β•Ύ 2
β”‚ └─╼ ...
β”œβ”€β•Ό 3
β”œβ”€β•Ό 4
└─╼ ...
--- add_edge(2, 3) ---
╙── 1 β•Ύ 1, 2
β”œβ”€β•Ό 2 β•Ύ 2
β”‚ β”œβ”€β•Ό 3 β•Ύ 1
β”‚ └─╼ ...
β”œβ”€β•Ό 4
└─╼ ...
--- add_edge(2, 4) ---
╙── 1 β•Ύ 1, 2
β”œβ”€β•Ό 2 β•Ύ 2
β”‚ β”œβ”€β•Ό 3 β•Ύ 1
β”‚ β”œβ”€β•Ό 4 β•Ύ 1
β”‚ └─╼ ...
└─╼ ...
--- add_edge(3, 1) ---
╙── 2 β•Ύ 1, 2
β”œβ”€β•Ό 1 β•Ύ 1, 3
β”‚ β”œβ”€β•Ό 3 β•Ύ 2
β”‚ β”‚ └─╼ ...
β”‚ β”œβ”€β•Ό 4 β•Ύ 2
β”‚ └─╼ ...
└─╼ ...
--- add_edge(3, 2) ---
╙── 3 β•Ύ 1, 2
β”œβ”€β•Ό 1 β•Ύ 1, 2
β”‚ β”œβ”€β•Ό 2 β•Ύ 2, 3
β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 1
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(3, 3) ---
╙── 1 β•Ύ 1, 2, 3
β”œβ”€β•Ό 2 β•Ύ 2, 3
β”‚ β”œβ”€β•Ό 3 β•Ύ 1, 3
β”‚ β”‚ └─╼ ...
β”‚ β”œβ”€β•Ό 4 β•Ύ 1
β”‚ └─╼ ...
└─╼ ...
--- add_edge(3, 4) ---
╙── 1 β•Ύ 1, 2, 3
β”œβ”€β•Ό 2 β•Ύ 2, 3
β”‚ β”œβ”€β•Ό 3 β•Ύ 1, 3
β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 1, 2
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(4, 1) ---
╙── 2 β•Ύ 1, 2, 3
β”œβ”€β•Ό 1 β•Ύ 1, 3, 4
β”‚ β”œβ”€β•Ό 3 β•Ύ 2, 3
β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 1, 2
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(4, 2) ---
╙── 3 β•Ύ 1, 2, 3
β”œβ”€β•Ό 1 β•Ύ 1, 2, 4
β”‚ β”œβ”€β•Ό 2 β•Ύ 2, 3, 4
β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 1, 3
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(4, 3) ---
╙── 4 β•Ύ 1, 2, 3
β”œβ”€β•Ό 1 β•Ύ 1, 2, 3
β”‚ β”œβ”€β•Ό 2 β•Ύ 2, 3, 4
β”‚ β”‚ β”œβ”€β•Ό 3 β•Ύ 1, 3, 4
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(4, 4) ---
╙── 1 β•Ύ 1, 2, 3, 4
β”œβ”€β•Ό 2 β•Ύ 2, 3, 4
β”‚ β”œβ”€β•Ό 3 β•Ύ 1, 3, 4
β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 1, 2, 4
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
"""
).strip()
assert target == text
def test_write_network_text_iterative_add_undirected_edges():
"""
Walk through the cases going from a disconnected to fully connected graph
"""
graph = nx.Graph()
graph.add_nodes_from([1, 2, 3, 4])
lines = []
write = lines.append
write("--- initial state ---")
nx.write_network_text(graph, path=write, end="")
for i, j in product(graph.nodes, graph.nodes):
if i == j:
continue
write(f"--- add_edge({i}, {j}) ---")
graph.add_edge(i, j)
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- initial state ---
β•Ÿβ”€β”€ 1
β•Ÿβ”€β”€ 2
β•Ÿβ”€β”€ 3
╙── 4
--- add_edge(1, 2) ---
β•Ÿβ”€β”€ 3
β•Ÿβ”€β”€ 4
╙── 1
└── 2
--- add_edge(1, 3) ---
β•Ÿβ”€β”€ 4
╙── 2
└── 1
└── 3
--- add_edge(1, 4) ---
╙── 2
└── 1
β”œβ”€β”€ 3
└── 4
--- add_edge(2, 1) ---
╙── 2
└── 1
β”œβ”€β”€ 3
└── 4
--- add_edge(2, 3) ---
╙── 4
└── 1
β”œβ”€β”€ 2
β”‚ └── 3 ─ 1
└── ...
--- add_edge(2, 4) ---
╙── 3
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 3
β”‚ β”‚ └── 4 ─ 1
β”‚ └── ...
└── ...
--- add_edge(3, 1) ---
╙── 3
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 3
β”‚ β”‚ └── 4 ─ 1
β”‚ └── ...
└── ...
--- add_edge(3, 2) ---
╙── 3
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 3
β”‚ β”‚ └── 4 ─ 1
β”‚ └── ...
└── ...
--- add_edge(3, 4) ---
╙── 1
β”œβ”€β”€ 2
β”‚ β”œβ”€β”€ 3 ─ 1
β”‚ β”‚ └── 4 ─ 1, 2
β”‚ └── ...
└── ...
--- add_edge(4, 1) ---
╙── 1
β”œβ”€β”€ 2
β”‚ β”œβ”€β”€ 3 ─ 1
β”‚ β”‚ └── 4 ─ 1, 2
β”‚ └── ...
└── ...
--- add_edge(4, 2) ---
╙── 1
β”œβ”€β”€ 2
β”‚ β”œβ”€β”€ 3 ─ 1
β”‚ β”‚ └── 4 ─ 1, 2
β”‚ └── ...
└── ...
--- add_edge(4, 3) ---
╙── 1
β”œβ”€β”€ 2
β”‚ β”œβ”€β”€ 3 ─ 1
β”‚ β”‚ └── 4 ─ 1, 2
β”‚ └── ...
└── ...
"""
).strip()
assert target == text
def test_write_network_text_iterative_add_random_directed_edges():
"""
Walk through the cases going from a disconnected to fully connected graph
"""
rng = random.Random(724466096)
graph = nx.DiGraph()
graph.add_nodes_from([1, 2, 3, 4, 5])
possible_edges = list(product(graph.nodes, graph.nodes))
rng.shuffle(possible_edges)
graph.add_edges_from(possible_edges[0:8])
lines = []
write = lines.append
write("--- initial state ---")
nx.write_network_text(graph, path=write, end="")
for i, j in possible_edges[8:12]:
write(f"--- add_edge({i}, {j}) ---")
graph.add_edge(i, j)
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- initial state ---
╙── 3 β•Ύ 5
└─╼ 2 β•Ύ 2
β”œβ”€β•Ό 4 β•Ύ 4
β”‚ β”œβ”€β•Ό 5
β”‚ β”‚ β”œβ”€β•Ό 1 β•Ύ 1
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(4, 1) ---
╙── 3 β•Ύ 5
└─╼ 2 β•Ύ 2
β”œβ”€β•Ό 4 β•Ύ 4
β”‚ β”œβ”€β•Ό 5
β”‚ β”‚ β”œβ”€β•Ό 1 β•Ύ 1, 4
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(2, 1) ---
╙── 3 β•Ύ 5
└─╼ 2 β•Ύ 2
β”œβ”€β•Ό 4 β•Ύ 4
β”‚ β”œβ”€β•Ό 5
β”‚ β”‚ β”œβ”€β•Ό 1 β•Ύ 1, 4, 2
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(5, 2) ---
╙── 3 β•Ύ 5
└─╼ 2 β•Ύ 2, 5
β”œβ”€β•Ό 4 β•Ύ 4
β”‚ β”œβ”€β•Ό 5
β”‚ β”‚ β”œβ”€β•Ό 1 β•Ύ 1, 4, 2
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- add_edge(1, 5) ---
╙── 3 β•Ύ 5
└─╼ 2 β•Ύ 2, 5
β”œβ”€β•Ό 4 β•Ύ 4
β”‚ β”œβ”€β•Ό 5 β•Ύ 1
β”‚ β”‚ β”œβ”€β•Ό 1 β•Ύ 1, 4, 2
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
"""
).strip()
assert target == text
def test_write_network_text_nearly_forest():
g = nx.DiGraph()
g.add_edge(1, 2)
g.add_edge(1, 5)
g.add_edge(2, 3)
g.add_edge(3, 4)
g.add_edge(5, 6)
g.add_edge(6, 7)
g.add_edge(6, 8)
orig = g.copy()
g.add_edge(1, 8) # forward edge
g.add_edge(4, 2) # back edge
g.add_edge(6, 3) # cross edge
lines = []
write = lines.append
write("--- directed case ---")
nx.write_network_text(orig, path=write, end="")
write("--- add (1, 8), (4, 2), (6, 3) ---")
nx.write_network_text(g, path=write, end="")
write("--- undirected case ---")
nx.write_network_text(orig.to_undirected(), path=write, sources=[1], end="")
write("--- add (1, 8), (4, 2), (6, 3) ---")
nx.write_network_text(g.to_undirected(), path=write, sources=[1], end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- directed case ---
╙── 1
β”œβ”€β•Ό 2
β”‚ └─╼ 3
β”‚ └─╼ 4
└─╼ 5
└─╼ 6
β”œβ”€β•Ό 7
└─╼ 8
--- add (1, 8), (4, 2), (6, 3) ---
╙── 1
β”œβ”€β•Ό 2 β•Ύ 4
β”‚ └─╼ 3 β•Ύ 6
β”‚ └─╼ 4
β”‚ └─╼ ...
β”œβ”€β•Ό 5
β”‚ └─╼ 6
β”‚ β”œβ”€β•Ό 7
β”‚ β”œβ”€β•Ό 8 β•Ύ 1
β”‚ └─╼ ...
└─╼ ...
--- undirected case ---
╙── 1
β”œβ”€β”€ 2
β”‚ └── 3
β”‚ └── 4
└── 5
└── 6
β”œβ”€β”€ 7
└── 8
--- add (1, 8), (4, 2), (6, 3) ---
╙── 1
β”œβ”€β”€ 2
β”‚ β”œβ”€β”€ 3
β”‚ β”‚ β”œβ”€β”€ 4 ─ 2
β”‚ β”‚ └── 6
β”‚ β”‚ β”œβ”€β”€ 5 ─ 1
β”‚ β”‚ β”œβ”€β”€ 7
β”‚ β”‚ └── 8 ─ 1
β”‚ └── ...
└── ...
"""
).strip()
assert target == text
def test_write_network_text_complete_graph_ascii_only():
graph = nx.generators.complete_graph(5, create_using=nx.DiGraph)
lines = []
write = lines.append
write("--- directed case ---")
nx.write_network_text(graph, path=write, ascii_only=True, end="")
write("--- undirected case ---")
nx.write_network_text(graph.to_undirected(), path=write, ascii_only=True, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- directed case ---
+-- 0 <- 1, 2, 3, 4
|-> 1 <- 2, 3, 4
| |-> 2 <- 0, 3, 4
| | |-> 3 <- 0, 1, 4
| | | |-> 4 <- 0, 1, 2
| | | | L-> ...
| | | L-> ...
| | L-> ...
| L-> ...
L-> ...
--- undirected case ---
+-- 0
|-- 1
| |-- 2 - 0
| | |-- 3 - 0, 1
| | | L-- 4 - 0, 1, 2
| | L-- ...
| L-- ...
L-- ...
"""
).strip()
assert target == text
def test_write_network_text_with_labels():
graph = nx.generators.complete_graph(5, create_using=nx.DiGraph)
for n in graph.nodes:
graph.nodes[n]["label"] = f"Node(n={n})"
lines = []
write = lines.append
nx.write_network_text(graph, path=write, with_labels=True, ascii_only=False, end="")
text = "\n".join(lines)
print(text)
# Non trees with labels can get somewhat out of hand with network text
# because we need to immediately show every non-tree edge to the right
target = dedent(
"""
╙── Node(n=0) β•Ύ Node(n=1), Node(n=2), Node(n=3), Node(n=4)
β”œβ”€β•Ό Node(n=1) β•Ύ Node(n=2), Node(n=3), Node(n=4)
β”‚ β”œβ”€β•Ό Node(n=2) β•Ύ Node(n=0), Node(n=3), Node(n=4)
β”‚ β”‚ β”œβ”€β•Ό Node(n=3) β•Ύ Node(n=0), Node(n=1), Node(n=4)
β”‚ β”‚ β”‚ β”œβ”€β•Ό Node(n=4) β•Ύ Node(n=0), Node(n=1), Node(n=2)
β”‚ β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
"""
).strip()
assert target == text
def test_write_network_text_complete_graphs():
lines = []
write = lines.append
for k in [0, 1, 2, 3, 4, 5]:
g = nx.generators.complete_graph(k)
write(f"--- undirected k={k} ---")
nx.write_network_text(g, path=write, end="")
for k in [0, 1, 2, 3, 4, 5]:
g = nx.generators.complete_graph(k, nx.DiGraph)
write(f"--- directed k={k} ---")
nx.write_network_text(g, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- undirected k=0 ---
β•™
--- undirected k=1 ---
╙── 0
--- undirected k=2 ---
╙── 0
└── 1
--- undirected k=3 ---
╙── 0
β”œβ”€β”€ 1
β”‚ └── 2 ─ 0
└── ...
--- undirected k=4 ---
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 0
β”‚ β”‚ └── 3 ─ 0, 1
β”‚ └── ...
└── ...
--- undirected k=5 ---
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 0
β”‚ β”‚ β”œβ”€β”€ 3 ─ 0, 1
β”‚ β”‚ β”‚ └── 4 ─ 0, 1, 2
β”‚ β”‚ └── ...
β”‚ └── ...
└── ...
--- directed k=0 ---
β•™
--- directed k=1 ---
╙── 0
--- directed k=2 ---
╙── 0 β•Ύ 1
└─╼ 1
└─╼ ...
--- directed k=3 ---
╙── 0 β•Ύ 1, 2
β”œβ”€β•Ό 1 β•Ύ 2
β”‚ β”œβ”€β•Ό 2 β•Ύ 0
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- directed k=4 ---
╙── 0 β•Ύ 1, 2, 3
β”œβ”€β•Ό 1 β•Ύ 2, 3
β”‚ β”œβ”€β•Ό 2 β•Ύ 0, 3
β”‚ β”‚ β”œβ”€β•Ό 3 β•Ύ 0, 1
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- directed k=5 ---
╙── 0 β•Ύ 1, 2, 3, 4
β”œβ”€β•Ό 1 β•Ύ 2, 3, 4
β”‚ β”œβ”€β•Ό 2 β•Ύ 0, 3, 4
β”‚ β”‚ β”œβ”€β•Ό 3 β•Ύ 0, 1, 4
β”‚ β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 0, 1, 2
β”‚ β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
"""
).strip()
assert target == text
def test_write_network_text_multiple_sources():
g = nx.DiGraph()
g.add_edge(1, 2)
g.add_edge(1, 3)
g.add_edge(2, 4)
g.add_edge(3, 5)
g.add_edge(3, 6)
g.add_edge(5, 4)
g.add_edge(4, 1)
g.add_edge(1, 5)
lines = []
write = lines.append
# Use each node as the starting point to demonstrate how the representation
# changes.
nodes = sorted(g.nodes())
for n in nodes:
write(f"--- source node: {n} ---")
nx.write_network_text(g, path=write, sources=[n], end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- source node: 1 ---
╙── 1 β•Ύ 4
β”œβ”€β•Ό 2
β”‚ └─╼ 4 β•Ύ 5
β”‚ └─╼ ...
β”œβ”€β•Ό 3
β”‚ β”œβ”€β•Ό 5 β•Ύ 1
β”‚ β”‚ └─╼ ...
β”‚ └─╼ 6
└─╼ ...
--- source node: 2 ---
╙── 2 β•Ύ 1
└─╼ 4 β•Ύ 5
└─╼ 1
β”œβ”€β•Ό 3
β”‚ β”œβ”€β•Ό 5 β•Ύ 1
β”‚ β”‚ └─╼ ...
β”‚ └─╼ 6
└─╼ ...
--- source node: 3 ---
╙── 3 β•Ύ 1
β”œβ”€β•Ό 5 β•Ύ 1
β”‚ └─╼ 4 β•Ύ 2
β”‚ └─╼ 1
β”‚ β”œβ”€β•Ό 2
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ 6
--- source node: 4 ---
╙── 4 β•Ύ 2, 5
└─╼ 1
β”œβ”€β•Ό 2
β”‚ └─╼ ...
β”œβ”€β•Ό 3
β”‚ β”œβ”€β•Ό 5 β•Ύ 1
β”‚ β”‚ └─╼ ...
β”‚ └─╼ 6
└─╼ ...
--- source node: 5 ---
╙── 5 β•Ύ 3, 1
└─╼ 4 β•Ύ 2
└─╼ 1
β”œβ”€β•Ό 2
β”‚ └─╼ ...
β”œβ”€β•Ό 3
β”‚ β”œβ”€β•Ό 6
β”‚ └─╼ ...
└─╼ ...
--- source node: 6 ---
╙── 6 β•Ύ 3
"""
).strip()
assert target == text
def test_write_network_text_star_graph():
graph = nx.star_graph(5, create_using=nx.Graph)
lines = []
write = lines.append
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
╙── 1
└── 0
β”œβ”€β”€ 2
β”œβ”€β”€ 3
β”œβ”€β”€ 4
└── 5
"""
).strip()
assert target == text
def test_write_network_text_path_graph():
graph = nx.path_graph(3, create_using=nx.Graph)
lines = []
write = lines.append
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
╙── 0
└── 1
└── 2
"""
).strip()
assert target == text
def test_write_network_text_lollipop_graph():
graph = nx.lollipop_graph(4, 2, create_using=nx.Graph)
lines = []
write = lines.append
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
╙── 5
└── 4
└── 3
β”œβ”€β”€ 0
β”‚ β”œβ”€β”€ 1 ─ 3
β”‚ β”‚ └── 2 ─ 0, 3
β”‚ └── ...
└── ...
"""
).strip()
assert target == text
def test_write_network_text_wheel_graph():
graph = nx.wheel_graph(7, create_using=nx.Graph)
lines = []
write = lines.append
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
╙── 1
β”œβ”€β”€ 0
β”‚ β”œβ”€β”€ 2 ─ 1
β”‚ β”‚ └── 3 ─ 0
β”‚ β”‚ └── 4 ─ 0
β”‚ β”‚ └── 5 ─ 0
β”‚ β”‚ └── 6 ─ 0, 1
β”‚ └── ...
└── ...
"""
).strip()
assert target == text
def test_write_network_text_circular_ladder_graph():
graph = nx.circular_ladder_graph(4, create_using=nx.Graph)
lines = []
write = lines.append
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2
β”‚ β”‚ β”œβ”€β”€ 3 ─ 0
β”‚ β”‚ β”‚ └── 7
β”‚ β”‚ β”‚ β”œβ”€β”€ 6 ─ 2
β”‚ β”‚ β”‚ β”‚ └── 5 ─ 1
β”‚ β”‚ β”‚ β”‚ └── 4 ─ 0, 7
β”‚ β”‚ β”‚ └── ...
β”‚ β”‚ └── ...
β”‚ └── ...
└── ...
"""
).strip()
assert target == text
def test_write_network_text_dorogovtsev_goltsev_mendes_graph():
graph = nx.dorogovtsev_goltsev_mendes_graph(4, create_using=nx.Graph)
lines = []
write = lines.append
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
╙── 15
β”œβ”€β”€ 0
β”‚ β”œβ”€β”€ 1 ─ 15
β”‚ β”‚ β”œβ”€β”€ 2 ─ 0
β”‚ β”‚ β”‚ β”œβ”€β”€ 4 ─ 0
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 9 ─ 0
β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 22 ─ 0
β”‚ β”‚ β”‚ β”‚ β”‚ └── 38 ─ 4
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 13 ─ 2
β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 34 ─ 2
β”‚ β”‚ β”‚ β”‚ β”‚ └── 39 ─ 4
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 18 ─ 0
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 30 ─ 2
β”‚ β”‚ β”‚ β”‚ └── ...
β”‚ β”‚ β”‚ β”œβ”€β”€ 5 ─ 1
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 12 ─ 1
β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 29 ─ 1
β”‚ β”‚ β”‚ β”‚ β”‚ └── 40 ─ 5
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 14 ─ 2
β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 35 ─ 2
β”‚ β”‚ β”‚ β”‚ β”‚ └── 41 ─ 5
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 25 ─ 1
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 31 ─ 2
β”‚ β”‚ β”‚ β”‚ └── ...
β”‚ β”‚ β”‚ β”œβ”€β”€ 7 ─ 0
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 20 ─ 0
β”‚ β”‚ β”‚ β”‚ └── 32 ─ 2
β”‚ β”‚ β”‚ β”œβ”€β”€ 10 ─ 1
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 27 ─ 1
β”‚ β”‚ β”‚ β”‚ └── 33 ─ 2
β”‚ β”‚ β”‚ β”œβ”€β”€ 16 ─ 0
β”‚ β”‚ β”‚ β”œβ”€β”€ 23 ─ 1
β”‚ β”‚ β”‚ └── ...
β”‚ β”‚ β”œβ”€β”€ 3 ─ 0
β”‚ β”‚ β”‚ β”œβ”€β”€ 8 ─ 0
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 21 ─ 0
β”‚ β”‚ β”‚ β”‚ └── 36 ─ 3
β”‚ β”‚ β”‚ β”œβ”€β”€ 11 ─ 1
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ 28 ─ 1
β”‚ β”‚ β”‚ β”‚ └── 37 ─ 3
β”‚ β”‚ β”‚ β”œβ”€β”€ 17 ─ 0
β”‚ β”‚ β”‚ β”œβ”€β”€ 24 ─ 1
β”‚ β”‚ β”‚ └── ...
β”‚ β”‚ β”œβ”€β”€ 6 ─ 0
β”‚ β”‚ β”‚ β”œβ”€β”€ 19 ─ 0
β”‚ β”‚ β”‚ └── 26 ─ 1
β”‚ β”‚ └── ...
β”‚ └── ...
└── ...
"""
).strip()
assert target == text
def test_write_network_text_tree_max_depth():
orig = nx.balanced_tree(r=1, h=3, create_using=nx.DiGraph)
lines = []
write = lines.append
write("--- directed case, max_depth=0 ---")
nx.write_network_text(orig, path=write, end="", max_depth=0)
write("--- directed case, max_depth=1 ---")
nx.write_network_text(orig, path=write, end="", max_depth=1)
write("--- directed case, max_depth=2 ---")
nx.write_network_text(orig, path=write, end="", max_depth=2)
write("--- directed case, max_depth=3 ---")
nx.write_network_text(orig, path=write, end="", max_depth=3)
write("--- directed case, max_depth=4 ---")
nx.write_network_text(orig, path=write, end="", max_depth=4)
write("--- undirected case, max_depth=0 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
write("--- undirected case, max_depth=1 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
write("--- undirected case, max_depth=2 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
write("--- undirected case, max_depth=3 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
write("--- undirected case, max_depth=4 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=4)
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- directed case, max_depth=0 ---
β•™ ...
--- directed case, max_depth=1 ---
╙── 0
└─╼ ...
--- directed case, max_depth=2 ---
╙── 0
└─╼ 1
└─╼ ...
--- directed case, max_depth=3 ---
╙── 0
└─╼ 1
└─╼ 2
└─╼ ...
--- directed case, max_depth=4 ---
╙── 0
└─╼ 1
└─╼ 2
└─╼ 3
--- undirected case, max_depth=0 ---
β•™ ...
--- undirected case, max_depth=1 ---
╙── 0 ─ 1
└── ...
--- undirected case, max_depth=2 ---
╙── 0
└── 1 ─ 2
└── ...
--- undirected case, max_depth=3 ---
╙── 0
└── 1
└── 2 ─ 3
└── ...
--- undirected case, max_depth=4 ---
╙── 0
└── 1
└── 2
└── 3
"""
).strip()
assert target == text
def test_write_network_text_graph_max_depth():
orig = nx.erdos_renyi_graph(10, 0.15, directed=True, seed=40392)
lines = []
write = lines.append
write("--- directed case, max_depth=None ---")
nx.write_network_text(orig, path=write, end="", max_depth=None)
write("--- directed case, max_depth=0 ---")
nx.write_network_text(orig, path=write, end="", max_depth=0)
write("--- directed case, max_depth=1 ---")
nx.write_network_text(orig, path=write, end="", max_depth=1)
write("--- directed case, max_depth=2 ---")
nx.write_network_text(orig, path=write, end="", max_depth=2)
write("--- directed case, max_depth=3 ---")
nx.write_network_text(orig, path=write, end="", max_depth=3)
write("--- undirected case, max_depth=None ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=None)
write("--- undirected case, max_depth=0 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
write("--- undirected case, max_depth=1 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
write("--- undirected case, max_depth=2 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
write("--- undirected case, max_depth=3 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- directed case, max_depth=None ---
β•Ÿβ”€β”€ 4
β•Ž β”œβ”€β•Ό 0 β•Ύ 3
β•Ž β”œβ”€β•Ό 5 β•Ύ 7
β•Ž β”‚ └─╼ 3
β•Ž β”‚ β”œβ”€β•Ό 1 β•Ύ 9
β•Ž β”‚ β”‚ └─╼ 9 β•Ύ 6
β•Ž β”‚ β”‚ β”œβ”€β•Ό 6
β•Ž β”‚ β”‚ β”‚ └─╼ ...
β•Ž β”‚ β”‚ β”œβ”€β•Ό 7 β•Ύ 4
β•Ž β”‚ β”‚ β”‚ β”œβ”€β•Ό 2
β•Ž β”‚ β”‚ β”‚ └─╼ ...
β•Ž β”‚ β”‚ └─╼ ...
β•Ž β”‚ └─╼ ...
β•Ž └─╼ ...
╙── 8
--- directed case, max_depth=0 ---
β•™ ...
--- directed case, max_depth=1 ---
β•Ÿβ”€β”€ 4
β•Ž └─╼ ...
╙── 8
--- directed case, max_depth=2 ---
β•Ÿβ”€β”€ 4
β•Ž β”œβ”€β•Ό 0 β•Ύ 3
β•Ž β”œβ”€β•Ό 5 β•Ύ 7
β•Ž β”‚ └─╼ ...
β•Ž └─╼ 7 β•Ύ 9
β•Ž └─╼ ...
╙── 8
--- directed case, max_depth=3 ---
β•Ÿβ”€β”€ 4
β•Ž β”œβ”€β•Ό 0 β•Ύ 3
β•Ž β”œβ”€β•Ό 5 β•Ύ 7
β•Ž β”‚ └─╼ 3
β•Ž β”‚ └─╼ ...
β•Ž └─╼ 7 β•Ύ 9
β•Ž β”œβ”€β•Ό 2
β•Ž └─╼ ...
╙── 8
--- undirected case, max_depth=None ---
β•Ÿβ”€β”€ 8
╙── 2
└── 7
β”œβ”€β”€ 4
β”‚ β”œβ”€β”€ 0
β”‚ β”‚ └── 3
β”‚ β”‚ β”œβ”€β”€ 1
β”‚ β”‚ β”‚ └── 9 ─ 7
β”‚ β”‚ β”‚ └── 6
β”‚ β”‚ └── 5 ─ 4, 7
β”‚ └── ...
└── ...
--- undirected case, max_depth=0 ---
β•™ ...
--- undirected case, max_depth=1 ---
β•Ÿβ”€β”€ 8
╙── 2 ─ 7
└── ...
--- undirected case, max_depth=2 ---
β•Ÿβ”€β”€ 8
╙── 2
└── 7 ─ 4, 5, 9
└── ...
--- undirected case, max_depth=3 ---
β•Ÿβ”€β”€ 8
╙── 2
└── 7
β”œβ”€β”€ 4 ─ 0, 5
β”‚ └── ...
β”œβ”€β”€ 5 ─ 4, 3
β”‚ └── ...
└── 9 ─ 1, 6
└── ...
"""
).strip()
assert target == text
def test_write_network_text_clique_max_depth():
orig = nx.complete_graph(5, nx.DiGraph)
lines = []
write = lines.append
write("--- directed case, max_depth=None ---")
nx.write_network_text(orig, path=write, end="", max_depth=None)
write("--- directed case, max_depth=0 ---")
nx.write_network_text(orig, path=write, end="", max_depth=0)
write("--- directed case, max_depth=1 ---")
nx.write_network_text(orig, path=write, end="", max_depth=1)
write("--- directed case, max_depth=2 ---")
nx.write_network_text(orig, path=write, end="", max_depth=2)
write("--- directed case, max_depth=3 ---")
nx.write_network_text(orig, path=write, end="", max_depth=3)
write("--- undirected case, max_depth=None ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=None)
write("--- undirected case, max_depth=0 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=0)
write("--- undirected case, max_depth=1 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=1)
write("--- undirected case, max_depth=2 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=2)
write("--- undirected case, max_depth=3 ---")
nx.write_network_text(orig.to_undirected(), path=write, end="", max_depth=3)
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- directed case, max_depth=None ---
╙── 0 β•Ύ 1, 2, 3, 4
β”œβ”€β•Ό 1 β•Ύ 2, 3, 4
β”‚ β”œβ”€β•Ό 2 β•Ύ 0, 3, 4
β”‚ β”‚ β”œβ”€β•Ό 3 β•Ύ 0, 1, 4
β”‚ β”‚ β”‚ β”œβ”€β•Ό 4 β•Ύ 0, 1, 2
β”‚ β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ β”‚ └─╼ ...
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- directed case, max_depth=0 ---
β•™ ...
--- directed case, max_depth=1 ---
╙── 0 β•Ύ 1, 2, 3, 4
└─╼ ...
--- directed case, max_depth=2 ---
╙── 0 β•Ύ 1, 2, 3, 4
β”œβ”€β•Ό 1 β•Ύ 2, 3, 4
β”‚ └─╼ ...
β”œβ”€β•Ό 2 β•Ύ 1, 3, 4
β”‚ └─╼ ...
β”œβ”€β•Ό 3 β•Ύ 1, 2, 4
β”‚ └─╼ ...
└─╼ 4 β•Ύ 1, 2, 3
└─╼ ...
--- directed case, max_depth=3 ---
╙── 0 β•Ύ 1, 2, 3, 4
β”œβ”€β•Ό 1 β•Ύ 2, 3, 4
β”‚ β”œβ”€β•Ό 2 β•Ύ 0, 3, 4
β”‚ β”‚ └─╼ ...
β”‚ β”œβ”€β•Ό 3 β•Ύ 0, 2, 4
β”‚ β”‚ └─╼ ...
β”‚ β”œβ”€β•Ό 4 β•Ύ 0, 2, 3
β”‚ β”‚ └─╼ ...
β”‚ └─╼ ...
└─╼ ...
--- undirected case, max_depth=None ---
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 0
β”‚ β”‚ β”œβ”€β”€ 3 ─ 0, 1
β”‚ β”‚ β”‚ └── 4 ─ 0, 1, 2
β”‚ β”‚ └── ...
β”‚ └── ...
└── ...
--- undirected case, max_depth=0 ---
β•™ ...
--- undirected case, max_depth=1 ---
╙── 0 ─ 1, 2, 3, 4
└── ...
--- undirected case, max_depth=2 ---
╙── 0
β”œβ”€β”€ 1 ─ 2, 3, 4
β”‚ └── ...
β”œβ”€β”€ 2 ─ 1, 3, 4
β”‚ └── ...
β”œβ”€β”€ 3 ─ 1, 2, 4
β”‚ └── ...
└── 4 ─ 1, 2, 3
--- undirected case, max_depth=3 ---
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 2 ─ 0, 3, 4
β”‚ β”‚ └── ...
β”‚ β”œβ”€β”€ 3 ─ 0, 2, 4
β”‚ β”‚ └── ...
β”‚ └── 4 ─ 0, 2, 3
└── ...
"""
).strip()
assert target == text
def test_write_network_text_custom_label():
# Create a directed forest with labels
graph = nx.erdos_renyi_graph(5, 0.4, directed=True, seed=359222358)
for node in graph.nodes:
graph.nodes[node]["label"] = f"Node({node})"
graph.nodes[node]["chr"] = chr(node + ord("a") - 1)
if node % 2 == 0:
graph.nodes[node]["part"] = chr(node + ord("a"))
lines = []
write = lines.append
write("--- when with_labels=True, uses the 'label' attr ---")
nx.write_network_text(graph, path=write, with_labels=True, end="", max_depth=None)
write("--- when with_labels=False, uses str(node) value ---")
nx.write_network_text(graph, path=write, with_labels=False, end="", max_depth=None)
write("--- when with_labels is a string, use that attr ---")
nx.write_network_text(graph, path=write, with_labels="chr", end="", max_depth=None)
write("--- fallback to str(node) when the attr does not exist ---")
nx.write_network_text(graph, path=write, with_labels="part", end="", max_depth=None)
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- when with_labels=True, uses the 'label' attr ---
╙── Node(1)
└─╼ Node(3) β•Ύ Node(2)
β”œβ”€β•Ό Node(0)
β”‚ β”œβ”€β•Ό Node(2) β•Ύ Node(3), Node(4)
β”‚ β”‚ └─╼ ...
β”‚ └─╼ Node(4)
β”‚ └─╼ ...
└─╼ ...
--- when with_labels=False, uses str(node) value ---
╙── 1
└─╼ 3 β•Ύ 2
β”œβ”€β•Ό 0
β”‚ β”œβ”€β•Ό 2 β•Ύ 3, 4
β”‚ β”‚ └─╼ ...
β”‚ └─╼ 4
β”‚ └─╼ ...
└─╼ ...
--- when with_labels is a string, use that attr ---
╙── a
└─╼ c β•Ύ b
β”œβ”€β•Ό `
β”‚ β”œβ”€β•Ό b β•Ύ c, d
β”‚ β”‚ └─╼ ...
β”‚ └─╼ d
β”‚ └─╼ ...
└─╼ ...
--- fallback to str(node) when the attr does not exist ---
╙── 1
└─╼ 3 β•Ύ c
β”œβ”€β•Ό a
β”‚ β”œβ”€β•Ό c β•Ύ 3, e
β”‚ β”‚ └─╼ ...
β”‚ └─╼ e
β”‚ └─╼ ...
└─╼ ...
"""
).strip()
assert target == text
def test_write_network_text_vertical_chains():
graph1 = nx.lollipop_graph(4, 2, create_using=nx.Graph)
graph1.add_edge(0, -1)
graph1.add_edge(-1, -2)
graph1.add_edge(-2, -3)
graph2 = graph1.to_directed()
graph2.remove_edges_from([(u, v) for u, v in graph2.edges if v > u])
lines = []
write = lines.append
write("--- Undirected UTF ---")
nx.write_network_text(graph1, path=write, end="", vertical_chains=True)
write("--- Undirected ASCI ---")
nx.write_network_text(
graph1, path=write, end="", vertical_chains=True, ascii_only=True
)
write("--- Directed UTF ---")
nx.write_network_text(graph2, path=write, end="", vertical_chains=True)
write("--- Directed ASCI ---")
nx.write_network_text(
graph2, path=write, end="", vertical_chains=True, ascii_only=True
)
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- Undirected UTF ---
╙── 5
β”‚
4
β”‚
3
β”œβ”€β”€ 0
β”‚ β”œβ”€β”€ 1 ─ 3
β”‚ β”‚ β”‚
β”‚ β”‚ 2 ─ 0, 3
β”‚ β”œβ”€β”€ -1
β”‚ β”‚ β”‚
β”‚ β”‚ -2
β”‚ β”‚ β”‚
β”‚ β”‚ -3
β”‚ └── ...
└── ...
--- Undirected ASCI ---
+-- 5
|
4
|
3
|-- 0
| |-- 1 - 3
| | |
| | 2 - 0, 3
| |-- -1
| | |
| | -2
| | |
| | -3
| L-- ...
L-- ...
--- Directed UTF ---
╙── 5
β•½
4
β•½
3
β”œβ”€β•Ό 0 β•Ύ 1, 2
β”‚ β•½
β”‚ -1
β”‚ β•½
β”‚ -2
β”‚ β•½
β”‚ -3
β”œβ”€β•Ό 1 β•Ύ 2
β”‚ └─╼ ...
└─╼ 2
└─╼ ...
--- Directed ASCI ---
+-- 5
!
4
!
3
|-> 0 <- 1, 2
| !
| -1
| !
| -2
| !
| -3
|-> 1 <- 2
| L-> ...
L-> 2
L-> ...
"""
).strip()
assert target == text
def test_collapse_directed():
graph = nx.balanced_tree(r=2, h=3, create_using=nx.DiGraph)
lines = []
write = lines.append
write("--- Original ---")
nx.write_network_text(graph, path=write, end="")
graph.nodes[1]["collapse"] = True
write("--- Collapse Node 1 ---")
nx.write_network_text(graph, path=write, end="")
write("--- Add alternate path (5, 3) to collapsed zone")
graph.add_edge(5, 3)
nx.write_network_text(graph, path=write, end="")
write("--- Collapse Node 0 ---")
graph.nodes[0]["collapse"] = True
nx.write_network_text(graph, path=write, end="")
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- Original ---
╙── 0
β”œβ”€β•Ό 1
β”‚ β”œβ”€β•Ό 3
β”‚ β”‚ β”œβ”€β•Ό 7
β”‚ β”‚ └─╼ 8
β”‚ └─╼ 4
β”‚ β”œβ”€β•Ό 9
β”‚ └─╼ 10
└─╼ 2
β”œβ”€β•Ό 5
β”‚ β”œβ”€β•Ό 11
β”‚ └─╼ 12
└─╼ 6
β”œβ”€β•Ό 13
└─╼ 14
--- Collapse Node 1 ---
╙── 0
β”œβ”€β•Ό 1
β”‚ └─╼ ...
└─╼ 2
β”œβ”€β•Ό 5
β”‚ β”œβ”€β•Ό 11
β”‚ └─╼ 12
└─╼ 6
β”œβ”€β•Ό 13
└─╼ 14
--- Add alternate path (5, 3) to collapsed zone
╙── 0
β”œβ”€β•Ό 1
β”‚ └─╼ ...
└─╼ 2
β”œβ”€β•Ό 5
β”‚ β”œβ”€β•Ό 11
β”‚ β”œβ”€β•Ό 12
β”‚ └─╼ 3 β•Ύ 1
β”‚ β”œβ”€β•Ό 7
β”‚ └─╼ 8
└─╼ 6
β”œβ”€β•Ό 13
└─╼ 14
--- Collapse Node 0 ---
╙── 0
└─╼ ...
"""
).strip()
assert target == text
def test_collapse_undirected():
graph = nx.balanced_tree(r=2, h=3, create_using=nx.Graph)
lines = []
write = lines.append
write("--- Original ---")
nx.write_network_text(graph, path=write, end="", sources=[0])
graph.nodes[1]["collapse"] = True
write("--- Collapse Node 1 ---")
nx.write_network_text(graph, path=write, end="", sources=[0])
write("--- Add alternate path (5, 3) to collapsed zone")
graph.add_edge(5, 3)
nx.write_network_text(graph, path=write, end="", sources=[0])
write("--- Collapse Node 0 ---")
graph.nodes[0]["collapse"] = True
nx.write_network_text(graph, path=write, end="", sources=[0])
text = "\n".join(lines)
print(text)
target = dedent(
"""
--- Original ---
╙── 0
β”œβ”€β”€ 1
β”‚ β”œβ”€β”€ 3
β”‚ β”‚ β”œβ”€β”€ 7
β”‚ β”‚ └── 8
β”‚ └── 4
β”‚ β”œβ”€β”€ 9
β”‚ └── 10
└── 2
β”œβ”€β”€ 5
β”‚ β”œβ”€β”€ 11
β”‚ └── 12
└── 6
β”œβ”€β”€ 13
└── 14
--- Collapse Node 1 ---
╙── 0
β”œβ”€β”€ 1 ─ 3, 4
β”‚ └── ...
└── 2
β”œβ”€β”€ 5
β”‚ β”œβ”€β”€ 11
β”‚ └── 12
└── 6
β”œβ”€β”€ 13
└── 14
--- Add alternate path (5, 3) to collapsed zone
╙── 0
β”œβ”€β”€ 1 ─ 3, 4
β”‚ └── ...
└── 2
β”œβ”€β”€ 5
β”‚ β”œβ”€β”€ 11
β”‚ β”œβ”€β”€ 12
β”‚ └── 3 ─ 1
β”‚ β”œβ”€β”€ 7
β”‚ └── 8
└── 6
β”œβ”€β”€ 13
└── 14
--- Collapse Node 0 ---
╙── 0 ─ 1, 2
└── ...
"""
).strip()
assert target == text
def generate_test_graphs():
"""
Generate a gauntlet of different test graphs with different properties
"""
import random
rng = random.Random(976689776)
num_randomized = 3
for directed in [0, 1]:
cls = nx.DiGraph if directed else nx.Graph
for num_nodes in range(17):
# Disconnected graph
graph = cls()
graph.add_nodes_from(range(num_nodes))
yield graph
# Randomize graphs
if num_nodes > 0:
for p in [0.1, 0.3, 0.5, 0.7, 0.9]:
for seed in range(num_randomized):
graph = nx.erdos_renyi_graph(
num_nodes, p, directed=directed, seed=rng
)
yield graph
yield nx.complete_graph(num_nodes, cls)
yield nx.path_graph(3, create_using=cls)
yield nx.balanced_tree(r=1, h=3, create_using=cls)
if not directed:
yield nx.circular_ladder_graph(4, create_using=cls)
yield nx.star_graph(5, create_using=cls)
yield nx.lollipop_graph(4, 2, create_using=cls)
yield nx.wheel_graph(7, create_using=cls)
yield nx.dorogovtsev_goltsev_mendes_graph(4, create_using=cls)
@pytest.mark.parametrize(
("vertical_chains", "ascii_only"),
tuple(
[
(vertical_chains, ascii_only)
for vertical_chains in [0, 1]
for ascii_only in [0, 1]
]
),
)
def test_network_text_round_trip(vertical_chains, ascii_only):
"""
Write the graph to network text format, then parse it back in, assert it is
the same as the original graph. Passing this test is strong validation of
both the format generator and parser.
"""
from networkx.readwrite.text import _parse_network_text
for graph in generate_test_graphs():
graph = nx.relabel_nodes(graph, {n: str(n) for n in graph.nodes})
lines = list(
nx.generate_network_text(
graph, vertical_chains=vertical_chains, ascii_only=ascii_only
)
)
new = _parse_network_text(lines)
try:
assert new.nodes == graph.nodes
assert new.edges == graph.edges
except Exception:
print("ERROR in round trip with graph")
nx.write_network_text(graph)
raise