1809 lines
55 KiB
Python
1809 lines
55 KiB
Python
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
|