comment here
This commit is contained in:
0
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/__init__.py
vendored
Normal file
0
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/__init__.py
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
242
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_agraph.py
vendored
Normal file
242
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_agraph.py
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
"""Unit tests for PyGraphviz interface."""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
pygraphviz = pytest.importorskip("pygraphviz")
|
||||
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import edges_equal, graphs_equal, nodes_equal
|
||||
|
||||
|
||||
class TestAGraph:
|
||||
def build_graph(self, G):
|
||||
edges = [("A", "B"), ("A", "C"), ("A", "C"), ("B", "C"), ("A", "D")]
|
||||
G.add_edges_from(edges)
|
||||
G.add_node("E")
|
||||
G.graph["metal"] = "bronze"
|
||||
return G
|
||||
|
||||
def assert_equal(self, G1, G2):
|
||||
assert nodes_equal(G1.nodes(), G2.nodes())
|
||||
assert edges_equal(G1.edges(), G2.edges())
|
||||
assert G1.graph["metal"] == G2.graph["metal"]
|
||||
|
||||
def agraph_checks(self, G):
|
||||
G = self.build_graph(G)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
self.assert_equal(G, H)
|
||||
|
||||
fd, fname = tempfile.mkstemp()
|
||||
nx.drawing.nx_agraph.write_dot(H, fname)
|
||||
Hin = nx.nx_agraph.read_dot(fname)
|
||||
self.assert_equal(H, Hin)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
with open(fname, "w") as fh:
|
||||
nx.drawing.nx_agraph.write_dot(H, fh)
|
||||
|
||||
with open(fname) as fh:
|
||||
Hin = nx.nx_agraph.read_dot(fh)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
self.assert_equal(H, Hin)
|
||||
|
||||
def test_from_agraph_name(self):
|
||||
G = nx.Graph(name="test")
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
assert G.name == "test"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"graph_class", (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
|
||||
)
|
||||
def test_from_agraph_create_using(self, graph_class):
|
||||
G = nx.path_graph(3)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A, create_using=graph_class)
|
||||
assert isinstance(H, graph_class)
|
||||
|
||||
def test_from_agraph_named_edges(self):
|
||||
# Create an AGraph from an existing (non-multi) Graph
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([0, 1])
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
# Add edge (+ name, given by key) to the AGraph
|
||||
A.add_edge(0, 1, key="foo")
|
||||
# Verify a.name roundtrips out to 'key' in from_agraph
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
assert isinstance(H, nx.Graph)
|
||||
assert ("0", "1", {"key": "foo"}) in H.edges(data=True)
|
||||
|
||||
def test_undirected(self):
|
||||
self.agraph_checks(nx.Graph())
|
||||
|
||||
def test_directed(self):
|
||||
self.agraph_checks(nx.DiGraph())
|
||||
|
||||
def test_multi_undirected(self):
|
||||
self.agraph_checks(nx.MultiGraph())
|
||||
|
||||
def test_multi_directed(self):
|
||||
self.agraph_checks(nx.MultiDiGraph())
|
||||
|
||||
def test_to_agraph_with_nodedata(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(1, color="red")
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
assert dict(A.nodes()[0].attr) == {"color": "red"}
|
||||
|
||||
@pytest.mark.parametrize("graph_class", (nx.Graph, nx.MultiGraph))
|
||||
def test_to_agraph_with_edgedata(self, graph_class):
|
||||
G = graph_class()
|
||||
G.add_nodes_from([0, 1])
|
||||
G.add_edge(0, 1, color="yellow")
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
assert dict(A.edges()[0].attr) == {"color": "yellow"}
|
||||
|
||||
def test_view_pygraphviz_path(self, tmp_path):
|
||||
G = nx.complete_graph(3)
|
||||
input_path = str(tmp_path / "graph.png")
|
||||
out_path, A = nx.nx_agraph.view_pygraphviz(G, path=input_path, show=False)
|
||||
assert out_path == input_path
|
||||
# Ensure file is not empty
|
||||
with open(input_path, "rb") as fh:
|
||||
data = fh.read()
|
||||
assert len(data) > 0
|
||||
|
||||
def test_view_pygraphviz_file_suffix(self, tmp_path):
|
||||
G = nx.complete_graph(3)
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, suffix=1, show=False)
|
||||
assert path[-6:] == "_1.png"
|
||||
|
||||
def test_view_pygraphviz(self):
|
||||
G = nx.Graph() # "An empty graph cannot be drawn."
|
||||
pytest.raises(nx.NetworkXException, nx.nx_agraph.view_pygraphviz, G)
|
||||
G = nx.barbell_graph(4, 6)
|
||||
nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
|
||||
def test_view_pygraphviz_edgelabel(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2, weight=7)
|
||||
G.add_edge(2, 3, weight=8)
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, edgelabel="weight", show=False)
|
||||
for edge in A.edges():
|
||||
assert edge.attr["weight"] in ("7", "8")
|
||||
|
||||
def test_view_pygraphviz_callable_edgelabel(self):
|
||||
G = nx.complete_graph(3)
|
||||
|
||||
def foo_label(data):
|
||||
return "foo"
|
||||
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, edgelabel=foo_label, show=False)
|
||||
for edge in A.edges():
|
||||
assert edge.attr["label"] == "foo"
|
||||
|
||||
def test_view_pygraphviz_multigraph_edgelabels(self):
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(0, 1, key=0, name="left_fork")
|
||||
G.add_edge(0, 1, key=1, name="right_fork")
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, edgelabel="name", show=False)
|
||||
edges = A.edges()
|
||||
assert len(edges) == 2
|
||||
for edge in edges:
|
||||
assert edge.attr["label"].strip() in ("left_fork", "right_fork")
|
||||
|
||||
def test_graph_with_reserved_keywords(self):
|
||||
# test attribute/keyword clash case for #1582
|
||||
# node: n
|
||||
# edges: u,v
|
||||
G = nx.Graph()
|
||||
G = self.build_graph(G)
|
||||
G.nodes["E"]["n"] = "keyword"
|
||||
G.edges[("A", "B")]["u"] = "keyword"
|
||||
G.edges[("A", "B")]["v"] = "keyword"
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
|
||||
def test_view_pygraphviz_no_added_attrs_to_input(self):
|
||||
G = nx.complete_graph(2)
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
assert G.graph == {}
|
||||
|
||||
@pytest.mark.xfail(reason="known bug in clean_attrs")
|
||||
def test_view_pygraphviz_leaves_input_graph_unmodified(self):
|
||||
G = nx.complete_graph(2)
|
||||
# Add entries to graph dict that to_agraph handles specially
|
||||
G.graph["node"] = {"width": "0.80"}
|
||||
G.graph["edge"] = {"fontsize": "14"}
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
assert G.graph == {"node": {"width": "0.80"}, "edge": {"fontsize": "14"}}
|
||||
|
||||
def test_graph_with_AGraph_attrs(self):
|
||||
G = nx.complete_graph(2)
|
||||
# Add entries to graph dict that to_agraph handles specially
|
||||
G.graph["node"] = {"width": "0.80"}
|
||||
G.graph["edge"] = {"fontsize": "14"}
|
||||
path, A = nx.nx_agraph.view_pygraphviz(G, show=False)
|
||||
# Ensure user-specified values are not lost
|
||||
assert dict(A.node_attr)["width"] == "0.80"
|
||||
assert dict(A.edge_attr)["fontsize"] == "14"
|
||||
|
||||
def test_round_trip_empty_graph(self):
|
||||
G = nx.Graph()
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
# assert graphs_equal(G, H)
|
||||
AA = nx.nx_agraph.to_agraph(H)
|
||||
HH = nx.nx_agraph.from_agraph(AA)
|
||||
assert graphs_equal(H, HH)
|
||||
G.graph["graph"] = {}
|
||||
G.graph["node"] = {}
|
||||
G.graph["edge"] = {}
|
||||
assert graphs_equal(G, HH)
|
||||
|
||||
@pytest.mark.xfail(reason="integer->string node conversion in round trip")
|
||||
def test_round_trip_integer_nodes(self):
|
||||
G = nx.complete_graph(3)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
H = nx.nx_agraph.from_agraph(A)
|
||||
assert graphs_equal(G, H)
|
||||
|
||||
def test_graphviz_alias(self):
|
||||
G = self.build_graph(nx.Graph())
|
||||
pos_graphviz = nx.nx_agraph.graphviz_layout(G)
|
||||
pos_pygraphviz = nx.nx_agraph.pygraphviz_layout(G)
|
||||
assert pos_graphviz == pos_pygraphviz
|
||||
|
||||
@pytest.mark.parametrize("root", range(5))
|
||||
def test_pygraphviz_layout_root(self, root):
|
||||
# NOTE: test depends on layout prog being deterministic
|
||||
G = nx.complete_graph(5)
|
||||
A = nx.nx_agraph.to_agraph(G)
|
||||
# Get layout with root arg is not None
|
||||
pygv_layout = nx.nx_agraph.pygraphviz_layout(G, prog="circo", root=root)
|
||||
# Equivalent layout directly on AGraph
|
||||
A.layout(args=f"-Groot={root}", prog="circo")
|
||||
# Parse AGraph layout
|
||||
a1_pos = tuple(float(v) for v in dict(A.get_node("1").attr)["pos"].split(","))
|
||||
assert pygv_layout[1] == a1_pos
|
||||
|
||||
def test_2d_layout(self):
|
||||
G = nx.Graph()
|
||||
G = self.build_graph(G)
|
||||
G.graph["dimen"] = 2
|
||||
pos = nx.nx_agraph.pygraphviz_layout(G, prog="neato")
|
||||
pos = list(pos.values())
|
||||
assert len(pos) == 5
|
||||
assert len(pos[0]) == 2
|
||||
|
||||
def test_3d_layout(self):
|
||||
G = nx.Graph()
|
||||
G = self.build_graph(G)
|
||||
G.graph["dimen"] = 3
|
||||
pos = nx.nx_agraph.pygraphviz_layout(G, prog="neato")
|
||||
pos = list(pos.values())
|
||||
assert len(pos) == 5
|
||||
assert len(pos[0]) == 3
|
||||
292
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_latex.py
vendored
Normal file
292
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_latex.py
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
def test_tikz_attributes():
|
||||
G = nx.path_graph(4, create_using=nx.DiGraph)
|
||||
pos = {n: (n, n) for n in G}
|
||||
|
||||
G.add_edge(0, 0)
|
||||
G.edges[(0, 0)]["label"] = "Loop"
|
||||
G.edges[(0, 0)]["label_options"] = "midway"
|
||||
|
||||
G.nodes[0]["style"] = "blue"
|
||||
G.nodes[1]["style"] = "line width=3,draw"
|
||||
G.nodes[2]["style"] = "circle,draw,blue!50"
|
||||
G.nodes[3]["label"] = "Stop"
|
||||
G.edges[(0, 1)]["label"] = "1st Step"
|
||||
G.edges[(0, 1)]["label_options"] = "near end"
|
||||
G.edges[(2, 3)]["label"] = "3rd Step"
|
||||
G.edges[(2, 3)]["label_options"] = "near start"
|
||||
G.edges[(2, 3)]["style"] = "bend left,green"
|
||||
G.edges[(1, 2)]["label"] = "2nd"
|
||||
G.edges[(1, 2)]["label_options"] = "pos=0.5"
|
||||
G.edges[(1, 2)]["style"] = ">->,bend right,line width=3,green!90"
|
||||
|
||||
output_tex = nx.to_latex(
|
||||
G,
|
||||
pos=pos,
|
||||
as_document=False,
|
||||
tikz_options="[scale=3]",
|
||||
node_options="style",
|
||||
edge_options="style",
|
||||
node_label="label",
|
||||
edge_label="label",
|
||||
edge_label_options="label_options",
|
||||
)
|
||||
expected_tex = r"""\begin{figure}
|
||||
\begin{tikzpicture}[scale=3]
|
||||
\draw
|
||||
(0, 0) node[blue] (0){0}
|
||||
(1, 1) node[line width=3,draw] (1){1}
|
||||
(2, 2) node[circle,draw,blue!50] (2){2}
|
||||
(3, 3) node (3){Stop};
|
||||
\begin{scope}[->]
|
||||
\draw (0) to node[near end] {1st Step} (1);
|
||||
\draw[loop,] (0) to node[midway] {Loop} (0);
|
||||
\draw[>->,bend right,line width=3,green!90] (1) to node[pos=0.5] {2nd} (2);
|
||||
\draw[bend left,green] (2) to node[near start] {3rd Step} (3);
|
||||
\end{scope}
|
||||
\end{tikzpicture}
|
||||
\end{figure}"""
|
||||
|
||||
assert output_tex == expected_tex
|
||||
# print(output_tex)
|
||||
# # Pretty way to assert that A.to_document() == expected_tex
|
||||
# content_same = True
|
||||
# for aa, bb in zip(expected_tex.split("\n"), output_tex.split("\n")):
|
||||
# if aa != bb:
|
||||
# content_same = False
|
||||
# print(f"-{aa}|\n+{bb}|")
|
||||
# assert content_same
|
||||
|
||||
|
||||
def test_basic_multiple_graphs():
|
||||
H1 = nx.path_graph(4)
|
||||
H2 = nx.complete_graph(4)
|
||||
H3 = nx.path_graph(8)
|
||||
H4 = nx.complete_graph(8)
|
||||
captions = [
|
||||
"Path on 4 nodes",
|
||||
"Complete graph on 4 nodes",
|
||||
"Path on 8 nodes",
|
||||
"Complete graph on 8 nodes",
|
||||
]
|
||||
labels = ["fig2a", "fig2b", "fig2c", "fig2d"]
|
||||
latex_code = nx.to_latex(
|
||||
[H1, H2, H3, H4],
|
||||
n_rows=2,
|
||||
sub_captions=captions,
|
||||
sub_labels=labels,
|
||||
)
|
||||
# print(latex_code)
|
||||
assert "begin{document}" in latex_code
|
||||
assert "begin{figure}" in latex_code
|
||||
assert latex_code.count("begin{subfigure}") == 4
|
||||
assert latex_code.count("tikzpicture") == 8
|
||||
assert latex_code.count("[-]") == 4
|
||||
|
||||
|
||||
def test_basic_tikz():
|
||||
expected_tex = r"""\documentclass{report}
|
||||
\usepackage{tikz}
|
||||
\usepackage{subcaption}
|
||||
|
||||
\begin{document}
|
||||
\begin{figure}
|
||||
\begin{subfigure}{0.5\textwidth}
|
||||
\begin{tikzpicture}[scale=2]
|
||||
\draw[gray!90]
|
||||
(0.749, 0.702) node[red!90] (0){0}
|
||||
(1.0, -0.014) node[red!90] (1){1}
|
||||
(-0.777, -0.705) node (2){2}
|
||||
(-0.984, 0.042) node (3){3}
|
||||
(-0.028, 0.375) node[cyan!90] (4){4}
|
||||
(-0.412, 0.888) node (5){5}
|
||||
(0.448, -0.856) node (6){6}
|
||||
(0.003, -0.431) node[cyan!90] (7){7};
|
||||
\begin{scope}[->,gray!90]
|
||||
\draw (0) to (4);
|
||||
\draw (0) to (5);
|
||||
\draw (0) to (6);
|
||||
\draw (0) to (7);
|
||||
\draw (1) to (4);
|
||||
\draw (1) to (5);
|
||||
\draw (1) to (6);
|
||||
\draw (1) to (7);
|
||||
\draw (2) to (4);
|
||||
\draw (2) to (5);
|
||||
\draw (2) to (6);
|
||||
\draw (2) to (7);
|
||||
\draw (3) to (4);
|
||||
\draw (3) to (5);
|
||||
\draw (3) to (6);
|
||||
\draw (3) to (7);
|
||||
\end{scope}
|
||||
\end{tikzpicture}
|
||||
\caption{My tikz number 1 of 2}\label{tikz_1_2}
|
||||
\end{subfigure}
|
||||
\begin{subfigure}{0.5\textwidth}
|
||||
\begin{tikzpicture}[scale=2]
|
||||
\draw[gray!90]
|
||||
(0.749, 0.702) node[green!90] (0){0}
|
||||
(1.0, -0.014) node[green!90] (1){1}
|
||||
(-0.777, -0.705) node (2){2}
|
||||
(-0.984, 0.042) node (3){3}
|
||||
(-0.028, 0.375) node[purple!90] (4){4}
|
||||
(-0.412, 0.888) node (5){5}
|
||||
(0.448, -0.856) node (6){6}
|
||||
(0.003, -0.431) node[purple!90] (7){7};
|
||||
\begin{scope}[->,gray!90]
|
||||
\draw (0) to (4);
|
||||
\draw (0) to (5);
|
||||
\draw (0) to (6);
|
||||
\draw (0) to (7);
|
||||
\draw (1) to (4);
|
||||
\draw (1) to (5);
|
||||
\draw (1) to (6);
|
||||
\draw (1) to (7);
|
||||
\draw (2) to (4);
|
||||
\draw (2) to (5);
|
||||
\draw (2) to (6);
|
||||
\draw (2) to (7);
|
||||
\draw (3) to (4);
|
||||
\draw (3) to (5);
|
||||
\draw (3) to (6);
|
||||
\draw (3) to (7);
|
||||
\end{scope}
|
||||
\end{tikzpicture}
|
||||
\caption{My tikz number 2 of 2}\label{tikz_2_2}
|
||||
\end{subfigure}
|
||||
\caption{A graph generated with python and latex.}
|
||||
\end{figure}
|
||||
\end{document}"""
|
||||
|
||||
edges = [
|
||||
(0, 4),
|
||||
(0, 5),
|
||||
(0, 6),
|
||||
(0, 7),
|
||||
(1, 4),
|
||||
(1, 5),
|
||||
(1, 6),
|
||||
(1, 7),
|
||||
(2, 4),
|
||||
(2, 5),
|
||||
(2, 6),
|
||||
(2, 7),
|
||||
(3, 4),
|
||||
(3, 5),
|
||||
(3, 6),
|
||||
(3, 7),
|
||||
]
|
||||
G = nx.DiGraph()
|
||||
G.add_nodes_from(range(8))
|
||||
G.add_edges_from(edges)
|
||||
pos = {
|
||||
0: (0.7490296171687696, 0.702353520257394),
|
||||
1: (1.0, -0.014221357723796535),
|
||||
2: (-0.7765783344161441, -0.7054170966808919),
|
||||
3: (-0.9842690223417624, 0.04177547602465483),
|
||||
4: (-0.02768523817180917, 0.3745724439551441),
|
||||
5: (-0.41154855146767433, 0.8880106515525136),
|
||||
6: (0.44780153389148264, -0.8561492709269164),
|
||||
7: (0.0032499953371383505, -0.43092436645809945),
|
||||
}
|
||||
|
||||
rc_node_color = {0: "red!90", 1: "red!90", 4: "cyan!90", 7: "cyan!90"}
|
||||
gp_node_color = {0: "green!90", 1: "green!90", 4: "purple!90", 7: "purple!90"}
|
||||
|
||||
H = G.copy()
|
||||
nx.set_node_attributes(G, rc_node_color, "color")
|
||||
nx.set_node_attributes(H, gp_node_color, "color")
|
||||
|
||||
sub_captions = ["My tikz number 1 of 2", "My tikz number 2 of 2"]
|
||||
sub_labels = ["tikz_1_2", "tikz_2_2"]
|
||||
|
||||
output_tex = nx.to_latex(
|
||||
[G, H],
|
||||
[pos, pos],
|
||||
tikz_options="[scale=2]",
|
||||
default_node_options="gray!90",
|
||||
default_edge_options="gray!90",
|
||||
node_options="color",
|
||||
sub_captions=sub_captions,
|
||||
sub_labels=sub_labels,
|
||||
caption="A graph generated with python and latex.",
|
||||
n_rows=2,
|
||||
as_document=True,
|
||||
)
|
||||
|
||||
assert output_tex == expected_tex
|
||||
# print(output_tex)
|
||||
# # Pretty way to assert that A.to_document() == expected_tex
|
||||
# content_same = True
|
||||
# for aa, bb in zip(expected_tex.split("\n"), output_tex.split("\n")):
|
||||
# if aa != bb:
|
||||
# content_same = False
|
||||
# print(f"-{aa}|\n+{bb}|")
|
||||
# assert content_same
|
||||
|
||||
|
||||
def test_exception_pos_single_graph(to_latex=nx.to_latex):
|
||||
# smoke test that pos can be a string
|
||||
G = nx.path_graph(4)
|
||||
to_latex(G, pos="pos")
|
||||
|
||||
# must include all nodes
|
||||
pos = {0: (1, 2), 1: (0, 1), 2: (2, 1)}
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(G, pos)
|
||||
|
||||
# must have 2 values
|
||||
pos[3] = (1, 2, 3)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(G, pos)
|
||||
pos[3] = 2
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(G, pos)
|
||||
|
||||
# check that passes with 2 values
|
||||
pos[3] = (3, 2)
|
||||
to_latex(G, pos)
|
||||
|
||||
|
||||
def test_exception_multiple_graphs(to_latex=nx.to_latex):
|
||||
G = nx.path_graph(3)
|
||||
pos_bad = {0: (1, 2), 1: (0, 1)}
|
||||
pos_OK = {0: (1, 2), 1: (0, 1), 2: (2, 1)}
|
||||
fourG = [G, G, G, G]
|
||||
fourpos = [pos_OK, pos_OK, pos_OK, pos_OK]
|
||||
|
||||
# input single dict to use for all graphs
|
||||
to_latex(fourG, pos_OK)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(fourG, pos_bad)
|
||||
|
||||
# input list of dicts to use for all graphs
|
||||
to_latex(fourG, fourpos)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(fourG, [pos_bad, pos_bad, pos_bad, pos_bad])
|
||||
|
||||
# every pos dict must include all nodes
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(fourG, [pos_OK, pos_OK, pos_bad, pos_OK])
|
||||
|
||||
# test sub_captions and sub_labels (len must match Gbunch)
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(fourG, fourpos, sub_captions=["hi", "hi"])
|
||||
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
to_latex(fourG, fourpos, sub_labels=["hi", "hi"])
|
||||
|
||||
# all pass
|
||||
to_latex(fourG, fourpos, sub_captions=["hi"] * 4, sub_labels=["lbl"] * 4)
|
||||
|
||||
|
||||
def test_exception_multigraph():
|
||||
G = nx.path_graph(4, create_using=nx.MultiGraph)
|
||||
G.add_edge(1, 2)
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
nx.to_latex(G)
|
||||
470
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_layout.py
vendored
Normal file
470
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_layout.py
vendored
Normal file
@@ -0,0 +1,470 @@
|
||||
"""Unit tests for layout functions."""
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
pytest.importorskip("scipy")
|
||||
|
||||
|
||||
class TestLayout:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.Gi = nx.grid_2d_graph(5, 5)
|
||||
cls.Gs = nx.Graph()
|
||||
nx.add_path(cls.Gs, "abcdef")
|
||||
cls.bigG = nx.grid_2d_graph(25, 25) # > 500 nodes for sparse
|
||||
|
||||
def test_spring_fixed_without_pos(self):
|
||||
G = nx.path_graph(4)
|
||||
pytest.raises(ValueError, nx.spring_layout, G, fixed=[0])
|
||||
pos = {0: (1, 1), 2: (0, 0)}
|
||||
pytest.raises(ValueError, nx.spring_layout, G, fixed=[0, 1], pos=pos)
|
||||
nx.spring_layout(G, fixed=[0, 2], pos=pos) # No ValueError
|
||||
|
||||
def test_spring_init_pos(self):
|
||||
# Tests GH #2448
|
||||
import math
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (2, 0), (2, 3)])
|
||||
|
||||
init_pos = {0: (0.0, 0.0)}
|
||||
fixed_pos = [0]
|
||||
pos = nx.fruchterman_reingold_layout(G, pos=init_pos, fixed=fixed_pos)
|
||||
has_nan = any(math.isnan(c) for coords in pos.values() for c in coords)
|
||||
assert not has_nan, "values should not be nan"
|
||||
|
||||
def test_smoke_empty_graph(self):
|
||||
G = []
|
||||
nx.random_layout(G)
|
||||
nx.circular_layout(G)
|
||||
nx.planar_layout(G)
|
||||
nx.spring_layout(G)
|
||||
nx.fruchterman_reingold_layout(G)
|
||||
nx.spectral_layout(G)
|
||||
nx.shell_layout(G)
|
||||
nx.bipartite_layout(G, G)
|
||||
nx.spiral_layout(G)
|
||||
nx.multipartite_layout(G)
|
||||
nx.kamada_kawai_layout(G)
|
||||
|
||||
def test_smoke_int(self):
|
||||
G = self.Gi
|
||||
nx.random_layout(G)
|
||||
nx.circular_layout(G)
|
||||
nx.planar_layout(G)
|
||||
nx.spring_layout(G)
|
||||
nx.fruchterman_reingold_layout(G)
|
||||
nx.fruchterman_reingold_layout(self.bigG)
|
||||
nx.spectral_layout(G)
|
||||
nx.spectral_layout(G.to_directed())
|
||||
nx.spectral_layout(self.bigG)
|
||||
nx.spectral_layout(self.bigG.to_directed())
|
||||
nx.shell_layout(G)
|
||||
nx.spiral_layout(G)
|
||||
nx.kamada_kawai_layout(G)
|
||||
nx.kamada_kawai_layout(G, dim=1)
|
||||
nx.kamada_kawai_layout(G, dim=3)
|
||||
nx.arf_layout(G)
|
||||
|
||||
def test_smoke_string(self):
|
||||
G = self.Gs
|
||||
nx.random_layout(G)
|
||||
nx.circular_layout(G)
|
||||
nx.planar_layout(G)
|
||||
nx.spring_layout(G)
|
||||
nx.fruchterman_reingold_layout(G)
|
||||
nx.spectral_layout(G)
|
||||
nx.shell_layout(G)
|
||||
nx.spiral_layout(G)
|
||||
nx.kamada_kawai_layout(G)
|
||||
nx.kamada_kawai_layout(G, dim=1)
|
||||
nx.kamada_kawai_layout(G, dim=3)
|
||||
nx.arf_layout(G)
|
||||
|
||||
def check_scale_and_center(self, pos, scale, center):
|
||||
center = np.array(center)
|
||||
low = center - scale
|
||||
hi = center + scale
|
||||
vpos = np.array(list(pos.values()))
|
||||
length = vpos.max(0) - vpos.min(0)
|
||||
assert (length <= 2 * scale).all()
|
||||
assert (vpos >= low).all()
|
||||
assert (vpos <= hi).all()
|
||||
|
||||
def test_scale_and_center_arg(self):
|
||||
sc = self.check_scale_and_center
|
||||
c = (4, 5)
|
||||
G = nx.complete_graph(9)
|
||||
G.add_node(9)
|
||||
sc(nx.random_layout(G, center=c), scale=0.5, center=(4.5, 5.5))
|
||||
# rest can have 2*scale length: [-scale, scale]
|
||||
sc(nx.spring_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.spectral_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.circular_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.shell_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.spiral_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
sc(nx.kamada_kawai_layout(G, scale=2, center=c), scale=2, center=c)
|
||||
|
||||
c = (2, 3, 5)
|
||||
sc(nx.kamada_kawai_layout(G, dim=3, scale=2, center=c), scale=2, center=c)
|
||||
|
||||
def test_planar_layout_non_planar_input(self):
|
||||
G = nx.complete_graph(9)
|
||||
pytest.raises(nx.NetworkXException, nx.planar_layout, G)
|
||||
|
||||
def test_smoke_planar_layout_embedding_input(self):
|
||||
embedding = nx.PlanarEmbedding()
|
||||
embedding.set_data({0: [1, 2], 1: [0, 2], 2: [0, 1]})
|
||||
nx.planar_layout(embedding)
|
||||
|
||||
def test_default_scale_and_center(self):
|
||||
sc = self.check_scale_and_center
|
||||
c = (0, 0)
|
||||
G = nx.complete_graph(9)
|
||||
G.add_node(9)
|
||||
sc(nx.random_layout(G), scale=0.5, center=(0.5, 0.5))
|
||||
sc(nx.spring_layout(G), scale=1, center=c)
|
||||
sc(nx.spectral_layout(G), scale=1, center=c)
|
||||
sc(nx.circular_layout(G), scale=1, center=c)
|
||||
sc(nx.shell_layout(G), scale=1, center=c)
|
||||
sc(nx.spiral_layout(G), scale=1, center=c)
|
||||
sc(nx.kamada_kawai_layout(G), scale=1, center=c)
|
||||
|
||||
c = (0, 0, 0)
|
||||
sc(nx.kamada_kawai_layout(G, dim=3), scale=1, center=c)
|
||||
|
||||
def test_circular_planar_and_shell_dim_error(self):
|
||||
G = nx.path_graph(4)
|
||||
pytest.raises(ValueError, nx.circular_layout, G, dim=1)
|
||||
pytest.raises(ValueError, nx.shell_layout, G, dim=1)
|
||||
pytest.raises(ValueError, nx.shell_layout, G, dim=3)
|
||||
pytest.raises(ValueError, nx.planar_layout, G, dim=1)
|
||||
pytest.raises(ValueError, nx.planar_layout, G, dim=3)
|
||||
|
||||
def test_adjacency_interface_numpy(self):
|
||||
A = nx.to_numpy_array(self.Gs)
|
||||
pos = nx.drawing.layout._fruchterman_reingold(A)
|
||||
assert pos.shape == (6, 2)
|
||||
pos = nx.drawing.layout._fruchterman_reingold(A, dim=3)
|
||||
assert pos.shape == (6, 3)
|
||||
pos = nx.drawing.layout._sparse_fruchterman_reingold(A)
|
||||
assert pos.shape == (6, 2)
|
||||
|
||||
def test_adjacency_interface_scipy(self):
|
||||
A = nx.to_scipy_sparse_array(self.Gs, dtype="d")
|
||||
pos = nx.drawing.layout._sparse_fruchterman_reingold(A)
|
||||
assert pos.shape == (6, 2)
|
||||
pos = nx.drawing.layout._sparse_spectral(A)
|
||||
assert pos.shape == (6, 2)
|
||||
pos = nx.drawing.layout._sparse_fruchterman_reingold(A, dim=3)
|
||||
assert pos.shape == (6, 3)
|
||||
|
||||
def test_single_nodes(self):
|
||||
G = nx.path_graph(1)
|
||||
vpos = nx.shell_layout(G)
|
||||
assert not vpos[0].any()
|
||||
G = nx.path_graph(4)
|
||||
vpos = nx.shell_layout(G, [[0], [1, 2], [3]])
|
||||
assert not vpos[0].any()
|
||||
assert vpos[3].any() # ensure node 3 not at origin (#3188)
|
||||
assert np.linalg.norm(vpos[3]) <= 1 # ensure node 3 fits (#3753)
|
||||
vpos = nx.shell_layout(G, [[0], [1, 2], [3]], rotate=0)
|
||||
assert np.linalg.norm(vpos[3]) <= 1 # ensure node 3 fits (#3753)
|
||||
|
||||
def test_smoke_initial_pos_fruchterman_reingold(self):
|
||||
pos = nx.circular_layout(self.Gi)
|
||||
npos = nx.fruchterman_reingold_layout(self.Gi, pos=pos)
|
||||
|
||||
def test_smoke_initial_pos_arf(self):
|
||||
pos = nx.circular_layout(self.Gi)
|
||||
npos = nx.arf_layout(self.Gi, pos=pos)
|
||||
|
||||
def test_fixed_node_fruchterman_reingold(self):
|
||||
# Dense version (numpy based)
|
||||
pos = nx.circular_layout(self.Gi)
|
||||
npos = nx.spring_layout(self.Gi, pos=pos, fixed=[(0, 0)])
|
||||
assert tuple(pos[(0, 0)]) == tuple(npos[(0, 0)])
|
||||
# Sparse version (scipy based)
|
||||
pos = nx.circular_layout(self.bigG)
|
||||
npos = nx.spring_layout(self.bigG, pos=pos, fixed=[(0, 0)])
|
||||
for axis in range(2):
|
||||
assert pos[(0, 0)][axis] == pytest.approx(npos[(0, 0)][axis], abs=1e-7)
|
||||
|
||||
def test_center_parameter(self):
|
||||
G = nx.path_graph(1)
|
||||
nx.random_layout(G, center=(1, 1))
|
||||
vpos = nx.circular_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.planar_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.spring_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.fruchterman_reingold_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.spectral_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.shell_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
vpos = nx.spiral_layout(G, center=(1, 1))
|
||||
assert tuple(vpos[0]) == (1, 1)
|
||||
|
||||
def test_center_wrong_dimensions(self):
|
||||
G = nx.path_graph(1)
|
||||
assert id(nx.spring_layout) == id(nx.fruchterman_reingold_layout)
|
||||
pytest.raises(ValueError, nx.random_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.circular_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.planar_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spring_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spring_layout, G, dim=3, center=(1, 1))
|
||||
pytest.raises(ValueError, nx.spectral_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spectral_layout, G, dim=3, center=(1, 1))
|
||||
pytest.raises(ValueError, nx.shell_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.spiral_layout, G, center=(1, 1, 1))
|
||||
pytest.raises(ValueError, nx.kamada_kawai_layout, G, center=(1, 1, 1))
|
||||
|
||||
def test_empty_graph(self):
|
||||
G = nx.empty_graph()
|
||||
vpos = nx.random_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.circular_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.planar_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.bipartite_layout(G, G)
|
||||
assert vpos == {}
|
||||
vpos = nx.spring_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.fruchterman_reingold_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.spectral_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.shell_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.spiral_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.multipartite_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.kamada_kawai_layout(G, center=(1, 1))
|
||||
assert vpos == {}
|
||||
vpos = nx.arf_layout(G)
|
||||
assert vpos == {}
|
||||
|
||||
def test_bipartite_layout(self):
|
||||
G = nx.complete_bipartite_graph(3, 5)
|
||||
top, bottom = nx.bipartite.sets(G)
|
||||
|
||||
vpos = nx.bipartite_layout(G, top)
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
top_x = vpos[list(top)[0]][0]
|
||||
bottom_x = vpos[list(bottom)[0]][0]
|
||||
for node in top:
|
||||
assert vpos[node][0] == top_x
|
||||
for node in bottom:
|
||||
assert vpos[node][0] == bottom_x
|
||||
|
||||
vpos = nx.bipartite_layout(
|
||||
G, top, align="horizontal", center=(2, 2), scale=2, aspect_ratio=1
|
||||
)
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
top_y = vpos[list(top)[0]][1]
|
||||
bottom_y = vpos[list(bottom)[0]][1]
|
||||
for node in top:
|
||||
assert vpos[node][1] == top_y
|
||||
for node in bottom:
|
||||
assert vpos[node][1] == bottom_y
|
||||
|
||||
pytest.raises(ValueError, nx.bipartite_layout, G, top, align="foo")
|
||||
|
||||
def test_multipartite_layout(self):
|
||||
sizes = (0, 5, 7, 2, 8)
|
||||
G = nx.complete_multipartite_graph(*sizes)
|
||||
|
||||
vpos = nx.multipartite_layout(G)
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
start = 0
|
||||
for n in sizes:
|
||||
end = start + n
|
||||
assert all(vpos[start][0] == vpos[i][0] for i in range(start + 1, end))
|
||||
start += n
|
||||
|
||||
vpos = nx.multipartite_layout(G, align="horizontal", scale=2, center=(2, 2))
|
||||
assert len(vpos) == len(G)
|
||||
|
||||
start = 0
|
||||
for n in sizes:
|
||||
end = start + n
|
||||
assert all(vpos[start][1] == vpos[i][1] for i in range(start + 1, end))
|
||||
start += n
|
||||
|
||||
pytest.raises(ValueError, nx.multipartite_layout, G, align="foo")
|
||||
|
||||
def test_kamada_kawai_costfn_1d(self):
|
||||
costfn = nx.drawing.layout._kamada_kawai_costfn
|
||||
|
||||
pos = np.array([4.0, 7.0])
|
||||
invdist = 1 / np.array([[0.1, 2.0], [2.0, 0.3]])
|
||||
|
||||
cost, grad = costfn(pos, np, invdist, meanweight=0, dim=1)
|
||||
|
||||
assert cost == pytest.approx(((3 / 2.0 - 1) ** 2), abs=1e-7)
|
||||
assert grad[0] == pytest.approx((-0.5), abs=1e-7)
|
||||
assert grad[1] == pytest.approx(0.5, abs=1e-7)
|
||||
|
||||
def check_kamada_kawai_costfn(self, pos, invdist, meanwt, dim):
|
||||
costfn = nx.drawing.layout._kamada_kawai_costfn
|
||||
|
||||
cost, grad = costfn(pos.ravel(), np, invdist, meanweight=meanwt, dim=dim)
|
||||
|
||||
expected_cost = 0.5 * meanwt * np.sum(np.sum(pos, axis=0) ** 2)
|
||||
for i in range(pos.shape[0]):
|
||||
for j in range(i + 1, pos.shape[0]):
|
||||
diff = np.linalg.norm(pos[i] - pos[j])
|
||||
expected_cost += (diff * invdist[i][j] - 1.0) ** 2
|
||||
|
||||
assert cost == pytest.approx(expected_cost, abs=1e-7)
|
||||
|
||||
dx = 1e-4
|
||||
for nd in range(pos.shape[0]):
|
||||
for dm in range(pos.shape[1]):
|
||||
idx = nd * pos.shape[1] + dm
|
||||
ps = pos.flatten()
|
||||
|
||||
ps[idx] += dx
|
||||
cplus = costfn(ps, np, invdist, meanweight=meanwt, dim=pos.shape[1])[0]
|
||||
|
||||
ps[idx] -= 2 * dx
|
||||
cminus = costfn(ps, np, invdist, meanweight=meanwt, dim=pos.shape[1])[0]
|
||||
|
||||
assert grad[idx] == pytest.approx((cplus - cminus) / (2 * dx), abs=1e-5)
|
||||
|
||||
def test_kamada_kawai_costfn(self):
|
||||
invdist = 1 / np.array([[0.1, 2.1, 1.7], [2.1, 0.2, 0.6], [1.7, 0.6, 0.3]])
|
||||
meanwt = 0.3
|
||||
|
||||
# 2d
|
||||
pos = np.array([[1.3, -3.2], [2.7, -0.3], [5.1, 2.5]])
|
||||
|
||||
self.check_kamada_kawai_costfn(pos, invdist, meanwt, 2)
|
||||
|
||||
# 3d
|
||||
pos = np.array([[0.9, 8.6, -8.7], [-10, -0.5, -7.1], [9.1, -8.1, 1.6]])
|
||||
|
||||
self.check_kamada_kawai_costfn(pos, invdist, meanwt, 3)
|
||||
|
||||
def test_spiral_layout(self):
|
||||
|
||||
G = self.Gs
|
||||
|
||||
# a lower value of resolution should result in a more compact layout
|
||||
# intuitively, the total distance from the start and end nodes
|
||||
# via each node in between (transiting through each) will be less,
|
||||
# assuming rescaling does not occur on the computed node positions
|
||||
pos_standard = np.array(list(nx.spiral_layout(G, resolution=0.35).values()))
|
||||
pos_tighter = np.array(list(nx.spiral_layout(G, resolution=0.34).values()))
|
||||
distances = np.linalg.norm(pos_standard[:-1] - pos_standard[1:], axis=1)
|
||||
distances_tighter = np.linalg.norm(pos_tighter[:-1] - pos_tighter[1:], axis=1)
|
||||
assert sum(distances) > sum(distances_tighter)
|
||||
|
||||
# return near-equidistant points after the first value if set to true
|
||||
pos_equidistant = np.array(list(nx.spiral_layout(G, equidistant=True).values()))
|
||||
distances_equidistant = np.linalg.norm(
|
||||
pos_equidistant[:-1] - pos_equidistant[1:], axis=1
|
||||
)
|
||||
assert np.allclose(
|
||||
distances_equidistant[1:], distances_equidistant[-1], atol=0.01
|
||||
)
|
||||
|
||||
def test_spiral_layout_equidistant(self):
|
||||
G = nx.path_graph(10)
|
||||
pos = nx.spiral_layout(G, equidistant=True)
|
||||
# Extract individual node positions as an array
|
||||
p = np.array(list(pos.values()))
|
||||
# Elementwise-distance between node positions
|
||||
dist = np.linalg.norm(p[1:] - p[:-1], axis=1)
|
||||
assert np.allclose(np.diff(dist), 0, atol=1e-3)
|
||||
|
||||
def test_rescale_layout_dict(self):
|
||||
G = nx.empty_graph()
|
||||
vpos = nx.random_layout(G, center=(1, 1))
|
||||
assert nx.rescale_layout_dict(vpos) == {}
|
||||
|
||||
G = nx.empty_graph(2)
|
||||
vpos = {0: (0.0, 0.0), 1: (1.0, 1.0)}
|
||||
s_vpos = nx.rescale_layout_dict(vpos)
|
||||
assert np.linalg.norm([sum(x) for x in zip(*s_vpos.values())]) < 1e-6
|
||||
|
||||
G = nx.empty_graph(3)
|
||||
vpos = {0: (0, 0), 1: (1, 1), 2: (0.5, 0.5)}
|
||||
s_vpos = nx.rescale_layout_dict(vpos)
|
||||
|
||||
expectation = {
|
||||
0: np.array((-1, -1)),
|
||||
1: np.array((1, 1)),
|
||||
2: np.array((0, 0)),
|
||||
}
|
||||
for k, v in expectation.items():
|
||||
assert (s_vpos[k] == v).all()
|
||||
s_vpos = nx.rescale_layout_dict(vpos, scale=2)
|
||||
expectation = {
|
||||
0: np.array((-2, -2)),
|
||||
1: np.array((2, 2)),
|
||||
2: np.array((0, 0)),
|
||||
}
|
||||
for k, v in expectation.items():
|
||||
assert (s_vpos[k] == v).all()
|
||||
|
||||
def test_arf_layout_partial_input_test(self):
|
||||
"""
|
||||
Checks whether partial pos input still returns a proper position.
|
||||
"""
|
||||
G = self.Gs
|
||||
node = nx.utils.arbitrary_element(G)
|
||||
pos = nx.circular_layout(G)
|
||||
del pos[node]
|
||||
pos = nx.arf_layout(G, pos=pos)
|
||||
assert len(pos) == len(G)
|
||||
|
||||
def test_arf_layout_negative_a_check(self):
|
||||
"""
|
||||
Checks input parameters correctly raises errors. For example, `a` should be larger than 1
|
||||
"""
|
||||
G = self.Gs
|
||||
pytest.raises(ValueError, nx.arf_layout, G=G, a=-1)
|
||||
|
||||
|
||||
def test_multipartite_layout_nonnumeric_partition_labels():
|
||||
"""See gh-5123."""
|
||||
G = nx.Graph()
|
||||
G.add_node(0, subset="s0")
|
||||
G.add_node(1, subset="s0")
|
||||
G.add_node(2, subset="s1")
|
||||
G.add_node(3, subset="s1")
|
||||
G.add_edges_from([(0, 2), (0, 3), (1, 2)])
|
||||
pos = nx.multipartite_layout(G)
|
||||
assert len(pos) == len(G)
|
||||
|
||||
|
||||
def test_multipartite_layout_layer_order():
|
||||
"""Return the layers in sorted order if the layers of the multipartite
|
||||
graph are sortable. See gh-5691"""
|
||||
G = nx.Graph()
|
||||
for node, layer in zip(("a", "b", "c", "d", "e"), (2, 3, 1, 2, 4)):
|
||||
G.add_node(node, subset=layer)
|
||||
|
||||
# Horizontal alignment, therefore y-coord determines layers
|
||||
pos = nx.multipartite_layout(G, align="horizontal")
|
||||
|
||||
# Nodes "a" and "d" are in the same layer
|
||||
assert pos["a"][-1] == pos["d"][-1]
|
||||
# positions should be sorted according to layer
|
||||
assert pos["c"][-1] < pos["a"][-1] < pos["b"][-1] < pos["e"][-1]
|
||||
|
||||
# Make sure that multipartite_layout still works when layers are not sortable
|
||||
G.nodes["a"]["subset"] = "layer_0" # Can't sort mixed strs/ints
|
||||
pos_nosort = nx.multipartite_layout(G) # smoke test: this should not raise
|
||||
assert pos_nosort.keys() == pos.keys()
|
||||
190
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_pydot.py
vendored
Normal file
190
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_pydot.py
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
"""Unit tests for pydot drawing functions."""
|
||||
import os
|
||||
import tempfile
|
||||
from io import StringIO
|
||||
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.utils import graphs_equal
|
||||
|
||||
pydot = pytest.importorskip("pydot")
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
class TestPydot:
|
||||
def pydot_checks(self, G, prog):
|
||||
"""
|
||||
Validate :mod:`pydot`-based usage of the passed NetworkX graph with the
|
||||
passed basename of an external GraphViz command (e.g., `dot`, `neato`).
|
||||
"""
|
||||
|
||||
# Set the name of this graph to... "G". Failing to do so will
|
||||
# subsequently trip an assertion expecting this name.
|
||||
G.graph["name"] = "G"
|
||||
|
||||
# Add arbitrary nodes and edges to the passed empty graph.
|
||||
G.add_edges_from([("A", "B"), ("A", "C"), ("B", "C"), ("A", "D")])
|
||||
G.add_node("E")
|
||||
|
||||
# Validate layout of this graph with the passed GraphViz command.
|
||||
graph_layout = nx.nx_pydot.pydot_layout(G, prog=prog)
|
||||
assert isinstance(graph_layout, dict)
|
||||
|
||||
# Convert this graph into a "pydot.Dot" instance.
|
||||
P = nx.nx_pydot.to_pydot(G)
|
||||
|
||||
# Convert this "pydot.Dot" instance back into a graph of the same type.
|
||||
G2 = G.__class__(nx.nx_pydot.from_pydot(P))
|
||||
|
||||
# Validate the original and resulting graphs to be the same.
|
||||
assert graphs_equal(G, G2)
|
||||
|
||||
fd, fname = tempfile.mkstemp()
|
||||
|
||||
# Serialize this "pydot.Dot" instance to a temporary file in dot format
|
||||
P.write_raw(fname)
|
||||
|
||||
# Deserialize a list of new "pydot.Dot" instances back from this file.
|
||||
Pin_list = pydot.graph_from_dot_file(path=fname, encoding="utf-8")
|
||||
|
||||
# Validate this file to contain only one graph.
|
||||
assert len(Pin_list) == 1
|
||||
|
||||
# The single "pydot.Dot" instance deserialized from this file.
|
||||
Pin = Pin_list[0]
|
||||
|
||||
# Sorted list of all nodes in the original "pydot.Dot" instance.
|
||||
n1 = sorted(p.get_name() for p in P.get_node_list())
|
||||
|
||||
# Sorted list of all nodes in the deserialized "pydot.Dot" instance.
|
||||
n2 = sorted(p.get_name() for p in Pin.get_node_list())
|
||||
|
||||
# Validate these instances to contain the same nodes.
|
||||
assert n1 == n2
|
||||
|
||||
# Sorted list of all edges in the original "pydot.Dot" instance.
|
||||
e1 = sorted((e.get_source(), e.get_destination()) for e in P.get_edge_list())
|
||||
|
||||
# Sorted list of all edges in the original "pydot.Dot" instance.
|
||||
e2 = sorted((e.get_source(), e.get_destination()) for e in Pin.get_edge_list())
|
||||
|
||||
# Validate these instances to contain the same edges.
|
||||
assert e1 == e2
|
||||
|
||||
# Deserialize a new graph of the same type back from this file.
|
||||
Hin = nx.nx_pydot.read_dot(fname)
|
||||
Hin = G.__class__(Hin)
|
||||
|
||||
# Validate the original and resulting graphs to be the same.
|
||||
assert graphs_equal(G, Hin)
|
||||
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_undirected(self):
|
||||
self.pydot_checks(nx.Graph(), prog="neato")
|
||||
|
||||
def test_directed(self):
|
||||
self.pydot_checks(nx.DiGraph(), prog="dot")
|
||||
|
||||
def test_read_write(self):
|
||||
G = nx.MultiGraph()
|
||||
G.graph["name"] = "G"
|
||||
G.add_edge("1", "2", key="0") # read assumes strings
|
||||
fh = StringIO()
|
||||
nx.nx_pydot.write_dot(G, fh)
|
||||
fh.seek(0)
|
||||
H = nx.nx_pydot.read_dot(fh)
|
||||
assert graphs_equal(G, H)
|
||||
|
||||
|
||||
def test_pydot_issue_258():
|
||||
G = nx.Graph([("Example:A", 1)])
|
||||
with pytest.raises(ValueError):
|
||||
nx.nx_pydot.to_pydot(G)
|
||||
with pytest.raises(ValueError):
|
||||
nx.nx_pydot.pydot_layout(G)
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_node("1.2", style="filled", fillcolor="red:yellow")
|
||||
with pytest.raises(ValueError):
|
||||
nx.nx_pydot.to_pydot(G)
|
||||
G.remove_node("1.2")
|
||||
G.add_node("1.2", style="filled", fillcolor='"red:yellow"')
|
||||
assert (
|
||||
G.nodes.data() == nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).nodes.data()
|
||||
)
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("1", "2", foo="bar:1")
|
||||
with pytest.raises(ValueError):
|
||||
nx.nx_pydot.to_pydot(G)
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("1", "2", foo='"bar:1"')
|
||||
assert G["1"]["2"] == nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G))["1"]["2"]
|
||||
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge("1", "2", foo="b:1")
|
||||
G.add_edge("1", "2", bar="foo:foo")
|
||||
with pytest.raises(ValueError):
|
||||
nx.nx_pydot.to_pydot(G)
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge("1", "2", foo='"b:1"')
|
||||
G.add_edge("1", "2", bar='"foo:foo"')
|
||||
# Keys as integers aren't preserved in the conversion. They are read as strings.
|
||||
assert [attr for _, _, attr in G.edges.data()] == [
|
||||
attr
|
||||
for _, _, attr in nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).edges.data()
|
||||
]
|
||||
|
||||
G = nx.Graph()
|
||||
G.add_edge("1", "2")
|
||||
G["1"]["2"]["f:oo"] = "bar"
|
||||
with pytest.raises(ValueError):
|
||||
nx.nx_pydot.to_pydot(G)
|
||||
G = nx.Graph()
|
||||
G.add_edge("1", "2")
|
||||
G["1"]["2"]['"f:oo"'] = "bar"
|
||||
assert G["1"]["2"] == nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G))["1"]["2"]
|
||||
|
||||
G = nx.Graph([('"Example:A"', 1)])
|
||||
layout = nx.nx_pydot.pydot_layout(G)
|
||||
assert isinstance(layout, dict)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"graph_type", [nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph]
|
||||
)
|
||||
def test_hashable_pydot(graph_type):
|
||||
# gh-5790
|
||||
G = graph_type()
|
||||
G.add_edge("5", frozenset([1]), t='"Example:A"', l=False)
|
||||
G.add_edge("1", 2, w=True, t=("node1",), l=frozenset(["node1"]))
|
||||
G.add_edge("node", (3, 3), w="string")
|
||||
|
||||
assert [
|
||||
{"t": '"Example:A"', "l": "False"},
|
||||
{"w": "True", "t": "('node1',)", "l": "frozenset({'node1'})"},
|
||||
{"w": "string"},
|
||||
] == [
|
||||
attr
|
||||
for _, _, attr in nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).edges.data()
|
||||
]
|
||||
|
||||
assert {str(i) for i in G.nodes()} == set(
|
||||
nx.nx_pydot.from_pydot(nx.nx_pydot.to_pydot(G)).nodes
|
||||
)
|
||||
|
||||
|
||||
def test_pydot_numrical_name():
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([("A", "B"), (0, 1)])
|
||||
graph_layout = nx.nx_pydot.pydot_layout(G, prog="dot")
|
||||
assert isinstance(graph_layout, dict)
|
||||
assert "0" not in graph_layout
|
||||
assert 0 in graph_layout
|
||||
assert "1" not in graph_layout
|
||||
assert 1 in graph_layout
|
||||
assert "A" in graph_layout
|
||||
assert "B" in graph_layout
|
||||
791
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_pylab.py
vendored
Normal file
791
.CondaPkg/env/lib/python3.11/site-packages/networkx/drawing/tests/test_pylab.py
vendored
Normal file
@@ -0,0 +1,791 @@
|
||||
"""Unit tests for matplotlib drawing functions."""
|
||||
import itertools
|
||||
import os
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
mpl = pytest.importorskip("matplotlib")
|
||||
np = pytest.importorskip("numpy")
|
||||
mpl.use("PS")
|
||||
plt = pytest.importorskip("matplotlib.pyplot")
|
||||
plt.rcParams["text.usetex"] = False
|
||||
|
||||
|
||||
import networkx as nx
|
||||
|
||||
barbell = nx.barbell_graph(4, 6)
|
||||
|
||||
|
||||
def test_draw():
|
||||
try:
|
||||
functions = [
|
||||
nx.draw_circular,
|
||||
nx.draw_kamada_kawai,
|
||||
nx.draw_planar,
|
||||
nx.draw_random,
|
||||
nx.draw_spectral,
|
||||
nx.draw_spring,
|
||||
nx.draw_shell,
|
||||
]
|
||||
options = [{"node_color": "black", "node_size": 100, "width": 3}]
|
||||
for function, option in itertools.product(functions, options):
|
||||
function(barbell, **option)
|
||||
plt.savefig("test.ps")
|
||||
|
||||
finally:
|
||||
try:
|
||||
os.unlink("test.ps")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def test_draw_shell_nlist():
|
||||
try:
|
||||
nlist = [list(range(4)), list(range(4, 10)), list(range(10, 14))]
|
||||
nx.draw_shell(barbell, nlist=nlist)
|
||||
plt.savefig("test.ps")
|
||||
finally:
|
||||
try:
|
||||
os.unlink("test.ps")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def test_edge_colormap():
|
||||
colors = range(barbell.number_of_edges())
|
||||
nx.draw_spring(
|
||||
barbell, edge_color=colors, width=4, edge_cmap=plt.cm.Blues, with_labels=True
|
||||
)
|
||||
# plt.show()
|
||||
|
||||
|
||||
def test_arrows():
|
||||
nx.draw_spring(barbell.to_directed())
|
||||
# plt.show()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("edge_color", "expected"),
|
||||
(
|
||||
(None, "black"), # Default
|
||||
("r", "red"), # Non-default color string
|
||||
(["r"], "red"), # Single non-default color in a list
|
||||
((1.0, 1.0, 0.0), "yellow"), # single color as rgb tuple
|
||||
([(1.0, 1.0, 0.0)], "yellow"), # single color as rgb tuple in list
|
||||
((0, 1, 0, 1), "lime"), # single color as rgba tuple
|
||||
([(0, 1, 0, 1)], "lime"), # single color as rgba tuple in list
|
||||
("#0000ff", "blue"), # single color hex code
|
||||
(["#0000ff"], "blue"), # hex code in list
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("edgelist", (None, [(0, 1)]))
|
||||
def test_single_edge_color_undirected(edge_color, expected, edgelist):
|
||||
"""Tests ways of specifying all edges have a single color for edges
|
||||
drawn with a LineCollection"""
|
||||
|
||||
G = nx.path_graph(3)
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos=nx.random_layout(G), edgelist=edgelist, edge_color=edge_color
|
||||
)
|
||||
assert mpl.colors.same_color(drawn_edges.get_color(), expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("edge_color", "expected"),
|
||||
(
|
||||
(None, "black"), # Default
|
||||
("r", "red"), # Non-default color string
|
||||
(["r"], "red"), # Single non-default color in a list
|
||||
((1.0, 1.0, 0.0), "yellow"), # single color as rgb tuple
|
||||
([(1.0, 1.0, 0.0)], "yellow"), # single color as rgb tuple in list
|
||||
((0, 1, 0, 1), "lime"), # single color as rgba tuple
|
||||
([(0, 1, 0, 1)], "lime"), # single color as rgba tuple in list
|
||||
("#0000ff", "blue"), # single color hex code
|
||||
(["#0000ff"], "blue"), # hex code in list
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("edgelist", (None, [(0, 1)]))
|
||||
def test_single_edge_color_directed(edge_color, expected, edgelist):
|
||||
"""Tests ways of specifying all edges have a single color for edges drawn
|
||||
with FancyArrowPatches"""
|
||||
|
||||
G = nx.path_graph(3, create_using=nx.DiGraph)
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos=nx.random_layout(G), edgelist=edgelist, edge_color=edge_color
|
||||
)
|
||||
for fap in drawn_edges:
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), expected)
|
||||
|
||||
|
||||
def test_edge_color_tuple_interpretation():
|
||||
"""If edge_color is a sequence with the same length as edgelist, then each
|
||||
value in edge_color is mapped onto each edge via colormap."""
|
||||
G = nx.path_graph(6, create_using=nx.DiGraph)
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
|
||||
# num edges != 3 or 4 --> edge_color interpreted as rgb(a)
|
||||
for ec in ((0, 0, 1), (0, 0, 1, 1)):
|
||||
# More than 4 edges
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=ec)
|
||||
for fap in drawn_edges:
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), ec)
|
||||
# Fewer than 3 edges
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 1), (1, 2)], edge_color=ec
|
||||
)
|
||||
for fap in drawn_edges:
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), ec)
|
||||
|
||||
# num edges == 3, len(edge_color) == 4: interpreted as rgba
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 1), (1, 2), (2, 3)], edge_color=(0, 0, 1, 1)
|
||||
)
|
||||
for fap in drawn_edges:
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), "blue")
|
||||
|
||||
# num edges == 4, len(edge_color) == 3: interpreted as rgb
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 1), (1, 2), (2, 3), (3, 4)], edge_color=(0, 0, 1)
|
||||
)
|
||||
for fap in drawn_edges:
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), "blue")
|
||||
|
||||
# num edges == len(edge_color) == 3: interpreted with cmap, *not* as rgb
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 1), (1, 2), (2, 3)], edge_color=(0, 0, 1)
|
||||
)
|
||||
assert mpl.colors.same_color(
|
||||
drawn_edges[0].get_edgecolor(), drawn_edges[1].get_edgecolor()
|
||||
)
|
||||
for fap in drawn_edges:
|
||||
assert not mpl.colors.same_color(fap.get_edgecolor(), "blue")
|
||||
|
||||
# num edges == len(edge_color) == 4: interpreted with cmap, *not* as rgba
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos, edgelist=[(0, 1), (1, 2), (2, 3), (3, 4)], edge_color=(0, 0, 1, 1)
|
||||
)
|
||||
assert mpl.colors.same_color(
|
||||
drawn_edges[0].get_edgecolor(), drawn_edges[1].get_edgecolor()
|
||||
)
|
||||
assert mpl.colors.same_color(
|
||||
drawn_edges[2].get_edgecolor(), drawn_edges[3].get_edgecolor()
|
||||
)
|
||||
for fap in drawn_edges:
|
||||
assert not mpl.colors.same_color(fap.get_edgecolor(), "blue")
|
||||
|
||||
|
||||
def test_fewer_edge_colors_than_num_edges_directed():
|
||||
"""Test that the edge colors are cycled when there are fewer specified
|
||||
colors than edges."""
|
||||
G = barbell.to_directed()
|
||||
pos = nx.random_layout(barbell)
|
||||
edgecolors = ("r", "g", "b")
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=edgecolors)
|
||||
for fap, expected in zip(drawn_edges, itertools.cycle(edgecolors)):
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), expected)
|
||||
|
||||
|
||||
def test_more_edge_colors_than_num_edges_directed():
|
||||
"""Test that extra edge colors are ignored when there are more specified
|
||||
colors than edges."""
|
||||
G = nx.path_graph(4, create_using=nx.DiGraph) # 3 edges
|
||||
pos = nx.random_layout(barbell)
|
||||
edgecolors = ("r", "g", "b", "c") # 4 edge colors
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=edgecolors)
|
||||
for fap, expected in zip(drawn_edges, edgecolors[:-1]):
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), expected)
|
||||
|
||||
|
||||
def test_edge_color_string_with_gloabl_alpha_undirected():
|
||||
edge_collection = nx.draw_networkx_edges(
|
||||
barbell,
|
||||
pos=nx.random_layout(barbell),
|
||||
edgelist=[(0, 1), (1, 2)],
|
||||
edge_color="purple",
|
||||
alpha=0.2,
|
||||
)
|
||||
ec = edge_collection.get_color().squeeze() # as rgba tuple
|
||||
assert len(edge_collection.get_paths()) == 2
|
||||
assert mpl.colors.same_color(ec[:-1], "purple")
|
||||
assert ec[-1] == 0.2
|
||||
|
||||
|
||||
def test_edge_color_string_with_global_alpha_directed():
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
barbell.to_directed(),
|
||||
pos=nx.random_layout(barbell),
|
||||
edgelist=[(0, 1), (1, 2)],
|
||||
edge_color="purple",
|
||||
alpha=0.2,
|
||||
)
|
||||
assert len(drawn_edges) == 2
|
||||
for fap in drawn_edges:
|
||||
ec = fap.get_edgecolor() # As rgba tuple
|
||||
assert mpl.colors.same_color(ec[:-1], "purple")
|
||||
assert ec[-1] == 0.2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph))
|
||||
def test_edge_width_default_value(graph_type):
|
||||
"""Test the default linewidth for edges drawn either via LineCollection or
|
||||
FancyArrowPatches."""
|
||||
G = nx.path_graph(2, create_using=graph_type)
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos)
|
||||
if isinstance(drawn_edges, list): # directed case: list of FancyArrowPatch
|
||||
drawn_edges = drawn_edges[0]
|
||||
assert drawn_edges.get_linewidth() == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("edgewidth", "expected"),
|
||||
(
|
||||
(3, 3), # single-value, non-default
|
||||
([3], 3), # Single value as a list
|
||||
),
|
||||
)
|
||||
def test_edge_width_single_value_undirected(edgewidth, expected):
|
||||
G = nx.path_graph(4)
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, width=edgewidth)
|
||||
assert len(drawn_edges.get_paths()) == 3
|
||||
assert drawn_edges.get_linewidth() == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("edgewidth", "expected"),
|
||||
(
|
||||
(3, 3), # single-value, non-default
|
||||
([3], 3), # Single value as a list
|
||||
),
|
||||
)
|
||||
def test_edge_width_single_value_directed(edgewidth, expected):
|
||||
G = nx.path_graph(4, create_using=nx.DiGraph)
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, width=edgewidth)
|
||||
assert len(drawn_edges) == 3
|
||||
for fap in drawn_edges:
|
||||
assert fap.get_linewidth() == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"edgelist",
|
||||
(
|
||||
[(0, 1), (1, 2), (2, 3)], # one width specification per edge
|
||||
None, # fewer widths than edges - widths cycle
|
||||
[(0, 1), (1, 2)], # More widths than edges - unused widths ignored
|
||||
),
|
||||
)
|
||||
def test_edge_width_sequence(edgelist):
|
||||
G = barbell.to_directed()
|
||||
pos = nx.random_layout(G)
|
||||
widths = (0.5, 2.0, 12.0)
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, edgelist=edgelist, width=widths)
|
||||
for fap, expected_width in zip(drawn_edges, itertools.cycle(widths)):
|
||||
assert fap.get_linewidth() == expected_width
|
||||
|
||||
|
||||
def test_edge_color_with_edge_vmin_vmax():
|
||||
"""Test that edge_vmin and edge_vmax properly set the dynamic range of the
|
||||
color map when num edges == len(edge_colors)."""
|
||||
G = nx.path_graph(3, create_using=nx.DiGraph)
|
||||
pos = nx.random_layout(G)
|
||||
# Extract colors from the original (unscaled) colormap
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=[0, 1.0])
|
||||
orig_colors = [e.get_edgecolor() for e in drawn_edges]
|
||||
# Colors from scaled colormap
|
||||
drawn_edges = nx.draw_networkx_edges(
|
||||
G, pos, edge_color=[0.2, 0.8], edge_vmin=0.2, edge_vmax=0.8
|
||||
)
|
||||
scaled_colors = [e.get_edgecolor() for e in drawn_edges]
|
||||
assert mpl.colors.same_color(orig_colors, scaled_colors)
|
||||
|
||||
|
||||
def test_directed_edges_linestyle_default():
|
||||
"""Test default linestyle for edges drawn with FancyArrowPatches."""
|
||||
G = nx.path_graph(4, create_using=nx.DiGraph) # Graph with 3 edges
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
|
||||
# edge with default style
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos)
|
||||
assert len(drawn_edges) == 3
|
||||
for fap in drawn_edges:
|
||||
assert fap.get_linestyle() == "solid"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style",
|
||||
(
|
||||
"dashed", # edge with string style
|
||||
"--", # edge with simplified string style
|
||||
(1, (1, 1)), # edge with (offset, onoffseq) style
|
||||
),
|
||||
)
|
||||
def test_directed_edges_linestyle_single_value(style):
|
||||
"""Tests support for specifying linestyles with a single value to be applied to
|
||||
all edges in ``draw_networkx_edges`` for FancyArrowPatch outputs
|
||||
(e.g. directed edges)."""
|
||||
|
||||
G = nx.path_graph(4, create_using=nx.DiGraph) # Graph with 3 edges
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, style=style)
|
||||
assert len(drawn_edges) == 3
|
||||
for fap in drawn_edges:
|
||||
assert fap.get_linestyle() == style
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style_seq",
|
||||
(
|
||||
["dashed"], # edge with string style in list
|
||||
["--"], # edge with simplified string style in list
|
||||
[(1, (1, 1))], # edge with (offset, onoffseq) style in list
|
||||
["--", "-", ":"], # edges with styles for each edge
|
||||
["--", "-"], # edges with fewer styles than edges (styles cycle)
|
||||
["--", "-", ":", "-."], # edges with more styles than edges (extra unused)
|
||||
),
|
||||
)
|
||||
def test_directed_edges_linestyle_sequence(style_seq):
|
||||
"""Tests support for specifying linestyles with sequences in
|
||||
``draw_networkx_edges`` for FancyArrowPatch outputs (e.g. directed edges)."""
|
||||
|
||||
G = nx.path_graph(4, create_using=nx.DiGraph) # Graph with 3 edges
|
||||
pos = {n: (n, n) for n in range(len(G))}
|
||||
|
||||
drawn_edges = nx.draw_networkx_edges(G, pos, style=style_seq)
|
||||
assert len(drawn_edges) == 3
|
||||
for fap, style in zip(drawn_edges, itertools.cycle(style_seq)):
|
||||
assert fap.get_linestyle() == style
|
||||
|
||||
|
||||
def test_labels_and_colors():
|
||||
G = nx.cubical_graph()
|
||||
pos = nx.spring_layout(G) # positions for all nodes
|
||||
# nodes
|
||||
nx.draw_networkx_nodes(
|
||||
G, pos, nodelist=[0, 1, 2, 3], node_color="r", node_size=500, alpha=0.75
|
||||
)
|
||||
nx.draw_networkx_nodes(
|
||||
G,
|
||||
pos,
|
||||
nodelist=[4, 5, 6, 7],
|
||||
node_color="b",
|
||||
node_size=500,
|
||||
alpha=[0.25, 0.5, 0.75, 1.0],
|
||||
)
|
||||
# edges
|
||||
nx.draw_networkx_edges(G, pos, width=1.0, alpha=0.5)
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(0, 1), (1, 2), (2, 3), (3, 0)],
|
||||
width=8,
|
||||
alpha=0.5,
|
||||
edge_color="r",
|
||||
)
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)],
|
||||
width=8,
|
||||
alpha=0.5,
|
||||
edge_color="b",
|
||||
)
|
||||
nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)],
|
||||
arrows=True,
|
||||
min_source_margin=0.5,
|
||||
min_target_margin=0.75,
|
||||
width=8,
|
||||
edge_color="b",
|
||||
)
|
||||
# some math labels
|
||||
labels = {}
|
||||
labels[0] = r"$a$"
|
||||
labels[1] = r"$b$"
|
||||
labels[2] = r"$c$"
|
||||
labels[3] = r"$d$"
|
||||
labels[4] = r"$\alpha$"
|
||||
labels[5] = r"$\beta$"
|
||||
labels[6] = r"$\gamma$"
|
||||
labels[7] = r"$\delta$"
|
||||
nx.draw_networkx_labels(G, pos, labels, font_size=16)
|
||||
nx.draw_networkx_edge_labels(G, pos, edge_labels=None, rotate=False)
|
||||
nx.draw_networkx_edge_labels(G, pos, edge_labels={(4, 5): "4-5"})
|
||||
# plt.show()
|
||||
|
||||
|
||||
@pytest.mark.mpl_image_compare
|
||||
def test_house_with_colors():
|
||||
G = nx.house_graph()
|
||||
# explicitly set positions
|
||||
fig, ax = plt.subplots()
|
||||
pos = {0: (0, 0), 1: (1, 0), 2: (0, 1), 3: (1, 1), 4: (0.5, 2.0)}
|
||||
|
||||
# Plot nodes with different properties for the "wall" and "roof" nodes
|
||||
nx.draw_networkx_nodes(
|
||||
G,
|
||||
pos,
|
||||
node_size=3000,
|
||||
nodelist=[0, 1, 2, 3],
|
||||
node_color="tab:blue",
|
||||
)
|
||||
nx.draw_networkx_nodes(
|
||||
G, pos, node_size=2000, nodelist=[4], node_color="tab:orange"
|
||||
)
|
||||
nx.draw_networkx_edges(G, pos, alpha=0.5, width=6)
|
||||
# Customize axes
|
||||
ax.margins(0.11)
|
||||
plt.tight_layout()
|
||||
plt.axis("off")
|
||||
return fig
|
||||
|
||||
|
||||
def test_axes():
|
||||
fig, ax = plt.subplots()
|
||||
nx.draw(barbell, ax=ax)
|
||||
nx.draw_networkx_edge_labels(barbell, nx.circular_layout(barbell), ax=ax)
|
||||
|
||||
|
||||
def test_empty_graph():
|
||||
G = nx.Graph()
|
||||
nx.draw(G)
|
||||
|
||||
|
||||
def test_draw_empty_nodes_return_values():
|
||||
# See Issue #3833
|
||||
import matplotlib.collections # call as mpl.collections
|
||||
|
||||
G = nx.Graph([(1, 2), (2, 3)])
|
||||
DG = nx.DiGraph([(1, 2), (2, 3)])
|
||||
pos = nx.circular_layout(G)
|
||||
assert isinstance(
|
||||
nx.draw_networkx_nodes(G, pos, nodelist=[]), mpl.collections.PathCollection
|
||||
)
|
||||
assert isinstance(
|
||||
nx.draw_networkx_nodes(DG, pos, nodelist=[]), mpl.collections.PathCollection
|
||||
)
|
||||
|
||||
# drawing empty edges used to return an empty LineCollection or empty list.
|
||||
# Now it is always an empty list (because edges are now lists of FancyArrows)
|
||||
assert nx.draw_networkx_edges(G, pos, edgelist=[], arrows=True) == []
|
||||
assert nx.draw_networkx_edges(G, pos, edgelist=[], arrows=False) == []
|
||||
assert nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=False) == []
|
||||
assert nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=True) == []
|
||||
|
||||
|
||||
def test_multigraph_edgelist_tuples():
|
||||
# See Issue #3295
|
||||
G = nx.path_graph(3, create_using=nx.MultiDiGraph)
|
||||
nx.draw_networkx(G, edgelist=[(0, 1, 0)])
|
||||
nx.draw_networkx(G, edgelist=[(0, 1, 0)], node_size=[10, 20, 0])
|
||||
|
||||
|
||||
def test_alpha_iter():
|
||||
pos = nx.random_layout(barbell)
|
||||
fig = plt.figure()
|
||||
# with fewer alpha elements than nodes
|
||||
fig.add_subplot(131) # Each test in a new axis object
|
||||
nx.draw_networkx_nodes(barbell, pos, alpha=[0.1, 0.2])
|
||||
# with equal alpha elements and nodes
|
||||
num_nodes = len(barbell.nodes)
|
||||
alpha = [x / num_nodes for x in range(num_nodes)]
|
||||
colors = range(num_nodes)
|
||||
fig.add_subplot(132)
|
||||
nx.draw_networkx_nodes(barbell, pos, node_color=colors, alpha=alpha)
|
||||
# with more alpha elements than nodes
|
||||
alpha.append(1)
|
||||
fig.add_subplot(133)
|
||||
nx.draw_networkx_nodes(barbell, pos, alpha=alpha)
|
||||
|
||||
|
||||
def test_error_invalid_kwds():
|
||||
with pytest.raises(ValueError, match="Received invalid argument"):
|
||||
nx.draw(barbell, foo="bar")
|
||||
|
||||
|
||||
def test_draw_networkx_arrowsize_incorrect_size():
|
||||
G = nx.DiGraph([(0, 1), (0, 2), (0, 3), (1, 3)])
|
||||
arrowsize = [1, 2, 3]
|
||||
with pytest.raises(
|
||||
ValueError, match="arrowsize should have the same length as edgelist"
|
||||
):
|
||||
nx.draw(G, arrowsize=arrowsize)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("arrowsize", (30, [10, 20, 30]))
|
||||
def test_draw_edges_arrowsize(arrowsize):
|
||||
G = nx.DiGraph([(0, 1), (0, 2), (1, 2)])
|
||||
pos = {0: (0, 0), 1: (0, 1), 2: (1, 0)}
|
||||
edges = nx.draw_networkx_edges(G, pos=pos, arrowsize=arrowsize)
|
||||
|
||||
arrowsize = itertools.repeat(arrowsize) if isinstance(arrowsize, int) else arrowsize
|
||||
|
||||
for fap, expected in zip(edges, arrowsize):
|
||||
assert isinstance(fap, mpl.patches.FancyArrowPatch)
|
||||
assert fap.get_mutation_scale() == expected
|
||||
|
||||
|
||||
def test_np_edgelist():
|
||||
# see issue #4129
|
||||
nx.draw_networkx(barbell, edgelist=np.array([(0, 2), (0, 3)]))
|
||||
|
||||
|
||||
def test_draw_nodes_missing_node_from_position():
|
||||
G = nx.path_graph(3)
|
||||
pos = {0: (0, 0), 1: (1, 1)} # No position for node 2
|
||||
with pytest.raises(nx.NetworkXError, match="has no position"):
|
||||
nx.draw_networkx_nodes(G, pos)
|
||||
|
||||
|
||||
# NOTE: parametrizing on marker to test both branches of internal
|
||||
# nx.draw_networkx_edges.to_marker_edge function
|
||||
@pytest.mark.parametrize("node_shape", ("o", "s"))
|
||||
def test_draw_edges_min_source_target_margins(node_shape):
|
||||
"""Test that there is a wider gap between the node and the start of an
|
||||
incident edge when min_source_margin is specified.
|
||||
|
||||
This test checks that the use of min_{source/target}_margin kwargs result
|
||||
in shorter (more padding) between the edges and source and target nodes.
|
||||
As a crude visual example, let 's' and 't' represent source and target
|
||||
nodes, respectively:
|
||||
|
||||
Default:
|
||||
s-----------------------------t
|
||||
|
||||
With margins:
|
||||
s ----------------------- t
|
||||
|
||||
"""
|
||||
# Create a single axis object to get consistent pixel coords across
|
||||
# multiple draws
|
||||
fig, ax = plt.subplots()
|
||||
G = nx.DiGraph([(0, 1)])
|
||||
pos = {0: (0, 0), 1: (1, 0)} # horizontal layout
|
||||
# Get leftmost and rightmost points of the FancyArrowPatch object
|
||||
# representing the edge between nodes 0 and 1 (in pixel coordinates)
|
||||
default_patch = nx.draw_networkx_edges(G, pos, ax=ax, node_shape=node_shape)[0]
|
||||
default_extent = default_patch.get_extents().corners()[::2, 0]
|
||||
# Now, do the same but with "padding" for the source and target via the
|
||||
# min_{source/target}_margin kwargs
|
||||
padded_patch = nx.draw_networkx_edges(
|
||||
G,
|
||||
pos,
|
||||
ax=ax,
|
||||
node_shape=node_shape,
|
||||
min_source_margin=100,
|
||||
min_target_margin=100,
|
||||
)[0]
|
||||
padded_extent = padded_patch.get_extents().corners()[::2, 0]
|
||||
|
||||
# With padding, the left-most extent of the edge should be further to the
|
||||
# right
|
||||
assert padded_extent[0] > default_extent[0]
|
||||
# And the rightmost extent of the edge, further to the left
|
||||
assert padded_extent[1] < default_extent[1]
|
||||
|
||||
|
||||
def test_nonzero_selfloop_with_single_node():
|
||||
"""Ensure that selfloop extent is non-zero when there is only one node."""
|
||||
# Create explicit axis object for test
|
||||
fig, ax = plt.subplots()
|
||||
# Graph with single node + self loop
|
||||
G = nx.DiGraph()
|
||||
G.add_node(0)
|
||||
G.add_edge(0, 0)
|
||||
# Draw
|
||||
patch = nx.draw_networkx_edges(G, {0: (0, 0)})[0]
|
||||
# The resulting patch must have non-zero extent
|
||||
bbox = patch.get_extents()
|
||||
assert bbox.width > 0 and bbox.height > 0
|
||||
# Cleanup
|
||||
plt.delaxes(ax)
|
||||
|
||||
|
||||
def test_nonzero_selfloop_with_single_edge_in_edgelist():
|
||||
"""Ensure that selfloop extent is non-zero when only a single edge is
|
||||
specified in the edgelist.
|
||||
"""
|
||||
# Create explicit axis object for test
|
||||
fig, ax = plt.subplots()
|
||||
# Graph with selfloop
|
||||
G = nx.path_graph(2, create_using=nx.DiGraph)
|
||||
G.add_edge(1, 1)
|
||||
pos = {n: (n, n) for n in G.nodes}
|
||||
# Draw only the selfloop edge via the `edgelist` kwarg
|
||||
patch = nx.draw_networkx_edges(G, pos, edgelist=[(1, 1)])[0]
|
||||
# The resulting patch must have non-zero extent
|
||||
bbox = patch.get_extents()
|
||||
assert bbox.width > 0 and bbox.height > 0
|
||||
# Cleanup
|
||||
plt.delaxes(ax)
|
||||
|
||||
|
||||
def test_apply_alpha():
|
||||
"""Test apply_alpha when there is a mismatch between the number of
|
||||
supplied colors and elements.
|
||||
"""
|
||||
nodelist = [0, 1, 2]
|
||||
colorlist = ["r", "g", "b"]
|
||||
alpha = 0.5
|
||||
rgba_colors = nx.drawing.nx_pylab.apply_alpha(colorlist, alpha, nodelist)
|
||||
assert all(rgba_colors[:, -1] == alpha)
|
||||
|
||||
|
||||
def test_draw_edges_toggling_with_arrows_kwarg():
|
||||
"""
|
||||
The `arrows` keyword argument is used as a 3-way switch to select which
|
||||
type of object to use for drawing edges:
|
||||
- ``arrows=None`` -> default (FancyArrowPatches for directed, else LineCollection)
|
||||
- ``arrows=True`` -> FancyArrowPatches
|
||||
- ``arrows=False`` -> LineCollection
|
||||
"""
|
||||
import matplotlib.collections
|
||||
import matplotlib.patches
|
||||
|
||||
UG = nx.path_graph(3)
|
||||
DG = nx.path_graph(3, create_using=nx.DiGraph)
|
||||
pos = {n: (n, n) for n in UG}
|
||||
|
||||
# Use FancyArrowPatches when arrows=True, regardless of graph type
|
||||
for G in (UG, DG):
|
||||
edges = nx.draw_networkx_edges(G, pos, arrows=True)
|
||||
assert len(edges) == len(G.edges)
|
||||
assert isinstance(edges[0], mpl.patches.FancyArrowPatch)
|
||||
|
||||
# Use LineCollection when arrows=False, regardless of graph type
|
||||
for G in (UG, DG):
|
||||
edges = nx.draw_networkx_edges(G, pos, arrows=False)
|
||||
assert isinstance(edges, mpl.collections.LineCollection)
|
||||
|
||||
# Default behavior when arrows=None: FAPs for directed, LC's for undirected
|
||||
edges = nx.draw_networkx_edges(UG, pos)
|
||||
assert isinstance(edges, mpl.collections.LineCollection)
|
||||
edges = nx.draw_networkx_edges(DG, pos)
|
||||
assert len(edges) == len(G.edges)
|
||||
assert isinstance(edges[0], mpl.patches.FancyArrowPatch)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("drawing_func", (nx.draw, nx.draw_networkx))
|
||||
def test_draw_networkx_arrows_default_undirected(drawing_func):
|
||||
import matplotlib.collections
|
||||
|
||||
G = nx.path_graph(3)
|
||||
fig, ax = plt.subplots()
|
||||
drawing_func(G, ax=ax)
|
||||
assert any(isinstance(c, mpl.collections.LineCollection) for c in ax.collections)
|
||||
assert not ax.patches
|
||||
plt.delaxes(ax)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("drawing_func", (nx.draw, nx.draw_networkx))
|
||||
def test_draw_networkx_arrows_default_directed(drawing_func):
|
||||
import matplotlib.collections
|
||||
|
||||
G = nx.path_graph(3, create_using=nx.DiGraph)
|
||||
fig, ax = plt.subplots()
|
||||
drawing_func(G, ax=ax)
|
||||
assert not any(
|
||||
isinstance(c, mpl.collections.LineCollection) for c in ax.collections
|
||||
)
|
||||
assert ax.patches
|
||||
plt.delaxes(ax)
|
||||
|
||||
|
||||
def test_edgelist_kwarg_not_ignored():
|
||||
# See gh-4994
|
||||
G = nx.path_graph(3)
|
||||
G.add_edge(0, 0)
|
||||
fig, ax = plt.subplots()
|
||||
nx.draw(G, edgelist=[(0, 1), (1, 2)], ax=ax) # Exclude self-loop from edgelist
|
||||
assert not ax.patches
|
||||
plt.delaxes(ax)
|
||||
|
||||
|
||||
def test_draw_networkx_edge_label_multiedge_exception():
|
||||
"""
|
||||
draw_networkx_edge_labels should raise an informative error message when
|
||||
the edge label includes keys
|
||||
"""
|
||||
exception_msg = "draw_networkx_edge_labels does not support multiedges"
|
||||
G = nx.MultiGraph()
|
||||
G.add_edge(0, 1, weight=10)
|
||||
G.add_edge(0, 1, weight=20)
|
||||
edge_labels = nx.get_edge_attributes(G, "weight") # Includes edge keys
|
||||
pos = {n: (n, n) for n in G}
|
||||
with pytest.raises(nx.NetworkXError, match=exception_msg):
|
||||
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
|
||||
|
||||
|
||||
def test_draw_networkx_edge_label_empty_dict():
|
||||
"""Regression test for draw_networkx_edge_labels with empty dict. See
|
||||
gh-5372."""
|
||||
G = nx.path_graph(3)
|
||||
pos = {n: (n, n) for n in G.nodes}
|
||||
assert nx.draw_networkx_edge_labels(G, pos, edge_labels={}) == {}
|
||||
|
||||
|
||||
def test_draw_networkx_edges_undirected_selfloop_colors():
|
||||
"""When an edgelist is supplied along with a sequence of colors, check that
|
||||
the self-loops have the correct colors."""
|
||||
fig, ax = plt.subplots()
|
||||
# Edge list and corresponding colors
|
||||
edgelist = [(1, 3), (1, 2), (2, 3), (1, 1), (3, 3), (2, 2)]
|
||||
edge_colors = ["pink", "cyan", "black", "red", "blue", "green"]
|
||||
|
||||
G = nx.Graph(edgelist)
|
||||
pos = {n: (n, n) for n in G.nodes}
|
||||
nx.draw_networkx_edges(G, pos, ax=ax, edgelist=edgelist, edge_color=edge_colors)
|
||||
|
||||
# Verify that there are three fancy arrow patches (1 per self loop)
|
||||
assert len(ax.patches) == 3
|
||||
|
||||
# These are points that should be contained in the self loops. For example,
|
||||
# sl_points[0] will be (1, 1.1), which is inside the "path" of the first
|
||||
# self-loop but outside the others
|
||||
sl_points = np.array(edgelist[-3:]) + np.array([0, 0.1])
|
||||
|
||||
# Check that the mapping between self-loop locations and their colors is
|
||||
# correct
|
||||
for fap, clr, slp in zip(ax.patches, edge_colors[-3:], sl_points):
|
||||
assert fap.get_path().contains_point(slp)
|
||||
assert mpl.colors.same_color(fap.get_edgecolor(), clr)
|
||||
plt.delaxes(ax)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fap_only_kwarg", # Non-default values for kwargs that only apply to FAPs
|
||||
(
|
||||
{"arrowstyle": "-"},
|
||||
{"arrowsize": 20},
|
||||
{"connectionstyle": "arc3,rad=0.2"},
|
||||
{"min_source_margin": 10},
|
||||
{"min_target_margin": 10},
|
||||
),
|
||||
)
|
||||
def test_user_warnings_for_unused_edge_drawing_kwargs(fap_only_kwarg):
|
||||
"""Users should get a warning when they specify a non-default value for
|
||||
one of the kwargs that applies only to edges drawn with FancyArrowPatches,
|
||||
but FancyArrowPatches aren't being used under the hood."""
|
||||
G = nx.path_graph(3)
|
||||
pos = {n: (n, n) for n in G}
|
||||
fig, ax = plt.subplots()
|
||||
# By default, an undirected graph will use LineCollection to represent
|
||||
# the edges
|
||||
kwarg_name = list(fap_only_kwarg.keys())[0]
|
||||
with pytest.warns(
|
||||
UserWarning, match=f"\n\nThe {kwarg_name} keyword argument is not applicable"
|
||||
):
|
||||
nx.draw_networkx_edges(G, pos, ax=ax, **fap_only_kwarg)
|
||||
# FancyArrowPatches are always used when `arrows=True` is specified.
|
||||
# Check that warnings are *not* raised in this case
|
||||
with warnings.catch_warnings():
|
||||
# Escalate warnings -> errors so tests fail if warnings are raised
|
||||
warnings.simplefilter("error")
|
||||
nx.draw_networkx_edges(G, pos, ax=ax, arrows=True, **fap_only_kwarg)
|
||||
|
||||
plt.delaxes(ax)
|
||||
Reference in New Issue
Block a user