Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

View file

@ -0,0 +1,249 @@
"""Unit tests for PyGraphviz interface."""
import os
import tempfile
import pytest
pygraphviz = pytest.importorskip("pygraphviz")
from networkx.testing import assert_edges_equal, assert_nodes_equal, assert_graphs_equal
import networkx as nx
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)
fname = tempfile.mktemp()
nx.drawing.nx_agraph.write_dot(H, fname)
Hin = nx.nx_agraph.read_dot(fname)
os.unlink(fname)
self.assert_equal(H, Hin)
(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.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
def test_display_pygraphviz_deprecation_warning(self):
G = nx.complete_graph(2)
path_name, A = nx.nx_agraph.view_pygraphviz(G, show=False)
# Monkeypatch default_opener to prevent window opening
nx.utils.default_opener = lambda x: None
with pytest.warns(DeprecationWarning, match="display_pygraphviz is deprecated"):
with open(path_name, "wb") as fh:
nx.nx_agraph.display_pygraphviz(A, fh, prog="dot")

View file

@ -0,0 +1,406 @@
"""Unit tests for layout functions."""
import networkx as nx
from networkx.testing import almost_equal
import pytest
numpy = pytest.importorskip("numpy")
test_smoke_empty_graphscipy = 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
@staticmethod
def collect_node_distances(positions):
distances = []
prev_val = None
for k in positions:
if prev_val is not None:
diff = positions[k] - prev_val
distances.append(numpy.dot(diff, diff) ** 0.5)
prev_val = positions[k]
return distances
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)
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)
def check_scale_and_center(self, pos, scale, center):
center = numpy.array(center)
low = center - scale
hi = center + scale
vpos = numpy.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_matrix(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 numpy.linalg.norm(vpos[3]) <= 1 # ensure node 3 fits (#3753)
vpos = nx.shell_layout(G, [[0], [1, 2], [3]], rotate=0)
assert numpy.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_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 almost_equal(pos[(0, 0)][axis], npos[(0, 0)][axis])
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 == {}
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 = numpy.array([4.0, 7.0])
invdist = 1 / numpy.array([[0.1, 2.0], [2.0, 0.3]])
cost, grad = costfn(pos, numpy, invdist, meanweight=0, dim=1)
assert almost_equal(cost, ((3 / 2.0 - 1) ** 2))
assert almost_equal(grad[0], -0.5)
assert almost_equal(grad[1], 0.5)
def check_kamada_kawai_costfn(self, pos, invdist, meanwt, dim):
costfn = nx.drawing.layout._kamada_kawai_costfn
cost, grad = costfn(pos.ravel(), numpy, invdist, meanweight=meanwt, dim=dim)
expected_cost = 0.5 * meanwt * numpy.sum(numpy.sum(pos, axis=0) ** 2)
for i in range(pos.shape[0]):
for j in range(i + 1, pos.shape[0]):
diff = numpy.linalg.norm(pos[i] - pos[j])
expected_cost += (diff * invdist[i][j] - 1.0) ** 2
assert almost_equal(cost, expected_cost)
dx = 1e-4
for nd in range(pos.shape[0]):
for dm in range(pos.shape[1]):
idx = nd * pos.shape[1] + dm
pos0 = pos.flatten()
pos0[idx] += dx
cplus = costfn(
pos0, numpy, invdist, meanweight=meanwt, dim=pos.shape[1]
)[0]
pos0[idx] -= 2 * dx
cminus = costfn(
pos0, numpy, invdist, meanweight=meanwt, dim=pos.shape[1]
)[0]
assert almost_equal(grad[idx], (cplus - cminus) / (2 * dx), places=5)
def test_kamada_kawai_costfn(self):
invdist = 1 / numpy.array([[0.1, 2.1, 1.7], [2.1, 0.2, 0.6], [1.7, 0.6, 0.3]])
meanwt = 0.3
# 2d
pos = numpy.array([[1.3, -3.2], [2.7, -0.3], [5.1, 2.5]])
self.check_kamada_kawai_costfn(pos, invdist, meanwt, 2)
# 3d
pos = numpy.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 = nx.spiral_layout(G, resolution=0.35)
pos_tighter = nx.spiral_layout(G, resolution=0.34)
distances = self.collect_node_distances(pos_standard)
distances_tighter = self.collect_node_distances(pos_tighter)
assert sum(distances) > sum(distances_tighter)
# return near-equidistant points after the first value if set to true
pos_equidistant = nx.spiral_layout(G, equidistant=True)
distances_equidistant = self.collect_node_distances(pos_equidistant)
for d in range(1, len(distances_equidistant) - 1):
# test similarity to two decimal places
assert almost_equal(
distances_equidistant[d], distances_equidistant[d + 1], 2
)
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)
norm = numpy.linalg.norm
assert 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)
assert s_vpos == {0: (-1, -1), 1: (1, 1), 2: (0, 0)}
s_vpos = nx.rescale_layout_dict(vpos, scale=2)
assert s_vpos == {0: (-2, -2), 1: (2, 2), 2: (0, 0)}

View file

@ -0,0 +1,94 @@
"""Unit tests for pydot drawing functions."""
from io import StringIO
import tempfile
import networkx as nx
from networkx.testing import assert_graphs_equal
import pytest
pydot = pytest.importorskip("pydot")
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)
# Serialize this "pydot.Dot" instance to a temporary file in dot format
fname = tempfile.mktemp()
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)
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)

View file

@ -0,0 +1,257 @@
"""Unit tests for matplotlib drawing functions."""
import os
import itertools
import pytest
mpl = pytest.importorskip("matplotlib")
mpl.use("PS")
plt = pytest.importorskip("matplotlib.pyplot")
plt.rcParams["text.usetex"] = False
import networkx as nx
class TestPylab:
@classmethod
def setup_class(cls):
cls.G = nx.barbell_graph(4, 6)
def test_draw(self):
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(self.G, **option)
plt.savefig("test.ps")
finally:
try:
os.unlink("test.ps")
except OSError:
pass
def test_draw_shell_nlist(self):
try:
nlist = [list(range(4)), list(range(4, 10)), list(range(10, 14))]
nx.draw_shell(self.G, nlist=nlist)
plt.savefig("test.ps")
finally:
try:
os.unlink("test.ps")
except OSError:
pass
def test_edge_colormap(self):
colors = range(self.G.number_of_edges())
nx.draw_spring(
self.G, edge_color=colors, width=4, edge_cmap=plt.cm.Blues, with_labels=True
)
# plt.show()
def test_arrows(self):
nx.draw_spring(self.G.to_directed())
# plt.show()
def test_edge_colors_and_widths(self):
pos = nx.circular_layout(self.G)
for G in (self.G, self.G.to_directed()):
nx.draw_networkx_nodes(G, pos, node_color=[(1.0, 1.0, 0.2, 0.5)])
nx.draw_networkx_labels(G, pos)
# edge with default color and width
nx.draw_networkx_edges(
G, pos, edgelist=[(0, 1)], width=None, edge_color=None
)
# edges with global color strings and widths in lists
nx.draw_networkx_edges(
G, pos, edgelist=[(0, 2), (0, 3)], width=[3], edge_color=["r"]
)
# edges with color strings and widths for each edge
nx.draw_networkx_edges(
G, pos, edgelist=[(0, 2), (0, 3)], width=[1, 3], edge_color=["r", "b"]
)
# edges with fewer color strings and widths than edges
nx.draw_networkx_edges(
G,
pos,
edgelist=[(1, 2), (1, 3), (2, 3), (3, 4)],
width=[1, 3],
edge_color=["g", "m", "c"],
)
# edges with more color strings and widths than edges
nx.draw_networkx_edges(
G,
pos,
edgelist=[(3, 4)],
width=[1, 2, 3, 4],
edge_color=["r", "b", "g", "k"],
)
# with rgb tuple and 3 edges - is interpreted with cmap
nx.draw_networkx_edges(
G, pos, edgelist=[(4, 5), (5, 6), (6, 7)], edge_color=(1.0, 0.4, 0.3)
)
# with rgb tuple in list
nx.draw_networkx_edges(
G, pos, edgelist=[(7, 8), (8, 9)], edge_color=[(0.4, 1.0, 0.0)]
)
# with rgba tuple and 4 edges - is interpretted with cmap
nx.draw_networkx_edges(
G,
pos,
edgelist=[(9, 10), (10, 11), (10, 12), (10, 13)],
edge_color=(0.0, 1.0, 1.0, 0.5),
)
# with rgba tuple in list
nx.draw_networkx_edges(
G,
pos,
edgelist=[(9, 10), (10, 11), (10, 12), (10, 13)],
edge_color=[(0.0, 1.0, 1.0, 0.5)],
)
# with color string and global alpha
nx.draw_networkx_edges(
G, pos, edgelist=[(11, 12), (11, 13)], edge_color="purple", alpha=0.2
)
# with color string in a list
nx.draw_networkx_edges(
G, pos, edgelist=[(11, 12), (11, 13)], edge_color=["purple"]
)
# with single edge and hex color string
nx.draw_networkx_edges(G, pos, edgelist=[(12, 13)], edge_color="#1f78b4f0")
# edge_color as numeric using vmin, vmax
nx.draw_networkx_edges(
G,
pos,
edgelist=[(7, 8), (8, 9)],
edge_color=[0.2, 0.5],
edge_vmin=0.1,
edge_vmax=0.6,
)
# plt.show()
def test_labels_and_colors(self):
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)],
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()
def test_axes(self):
fig, ax = plt.subplots()
nx.draw(self.G, ax=ax)
def test_empty_graph(self):
G = nx.Graph()
nx.draw(G)
def test_draw_empty_nodes_return_values(self):
# See Issue #3833
from matplotlib.collections import PathCollection, LineCollection
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=[]), PathCollection)
assert isinstance(nx.draw_networkx_nodes(DG, pos, nodelist=[]), PathCollection)
# drawing empty edges either return an empty LineCollection or empty list.
assert isinstance(
nx.draw_networkx_edges(G, pos, edgelist=[], arrows=True), LineCollection
)
assert isinstance(
nx.draw_networkx_edges(G, pos, edgelist=[], arrows=False), LineCollection
)
assert isinstance(
nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=False), LineCollection
)
assert nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=True) == []
def test_multigraph_edgelist_tuples(self):
# 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(self):
pos = nx.random_layout(self.G)
# with fewer alpha elements than nodes
plt.subplot(131)
nx.draw_networkx_nodes(self.G, pos, alpha=[0.1, 0.2])
# with equal alpha elements and nodes
num_nodes = len(self.G.nodes)
alpha = [x / num_nodes for x in range(num_nodes)]
colors = range(num_nodes)
plt.subplot(132)
nx.draw_networkx_nodes(self.G, pos, node_color=colors, alpha=alpha)
# with more alpha elements than nodes
alpha.append(1)
plt.subplot(133)
nx.draw_networkx_nodes(self.G, pos, alpha=alpha)
def test_error_invalid_kwds(self):
with pytest.raises(ValueError, match="Received invalid argument"):
nx.draw(self.G, foo="bar")
def test_np_edgelist(self):
# see issue #4129
np = pytest.importorskip("numpy")
nx.draw_networkx(self.G, edgelist=np.array([(0, 2), (0, 3)]))