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,24 @@
import networkx as nx
def test_is_at_free():
is_at_free = nx.asteroidal.is_at_free
cycle = nx.cycle_graph(6)
assert not is_at_free(cycle)
path = nx.path_graph(6)
assert is_at_free(path)
small_graph = nx.complete_graph(2)
assert is_at_free(small_graph)
petersen = nx.petersen_graph()
assert not is_at_free(petersen)
clique = nx.complete_graph(6)
assert is_at_free(clique)
line_clique = nx.line_graph(clique)
assert not is_at_free(line_clique)

View file

@ -0,0 +1,152 @@
"""Unit tests for the :mod:`networkx.algorithms.boundary` module."""
from itertools import combinations
import networkx as nx
from networkx.testing import almost_equal, assert_edges_equal
from networkx import convert_node_labels_to_integers as cnlti
class TestNodeBoundary:
"""Unit tests for the :func:`~networkx.node_boundary` function."""
def test_null_graph(self):
"""Tests that the null graph has empty node boundaries."""
null = nx.null_graph()
assert nx.node_boundary(null, []) == set()
assert nx.node_boundary(null, [], []) == set()
assert nx.node_boundary(null, [1, 2, 3]) == set()
assert nx.node_boundary(null, [1, 2, 3], [4, 5, 6]) == set()
assert nx.node_boundary(null, [1, 2, 3], [3, 4, 5]) == set()
def test_path_graph(self):
P10 = cnlti(nx.path_graph(10), first_label=1)
assert nx.node_boundary(P10, []) == set()
assert nx.node_boundary(P10, [], []) == set()
assert nx.node_boundary(P10, [1, 2, 3]) == {4}
assert nx.node_boundary(P10, [4, 5, 6]) == {3, 7}
assert nx.node_boundary(P10, [3, 4, 5, 6, 7]) == {2, 8}
assert nx.node_boundary(P10, [8, 9, 10]) == {7}
assert nx.node_boundary(P10, [4, 5, 6], [9, 10]) == set()
def test_complete_graph(self):
K10 = cnlti(nx.complete_graph(10), first_label=1)
assert nx.node_boundary(K10, []) == set()
assert nx.node_boundary(K10, [], []) == set()
assert nx.node_boundary(K10, [1, 2, 3]) == {4, 5, 6, 7, 8, 9, 10}
assert nx.node_boundary(K10, [4, 5, 6]) == {1, 2, 3, 7, 8, 9, 10}
assert nx.node_boundary(K10, [3, 4, 5, 6, 7]) == {1, 2, 8, 9, 10}
assert nx.node_boundary(K10, [4, 5, 6], []) == set()
assert nx.node_boundary(K10, K10) == set()
assert nx.node_boundary(K10, [1, 2, 3], [3, 4, 5]) == {4, 5}
def test_petersen(self):
"""Check boundaries in the petersen graph
cheeger(G,k)=min(|bdy(S)|/|S| for |S|=k, 0<k<=|V(G)|/2)
"""
def cheeger(G, k):
return min(len(nx.node_boundary(G, nn)) / k for nn in combinations(G, k))
P = nx.petersen_graph()
assert almost_equal(cheeger(P, 1), 3.00, places=2)
assert almost_equal(cheeger(P, 2), 2.00, places=2)
assert almost_equal(cheeger(P, 3), 1.67, places=2)
assert almost_equal(cheeger(P, 4), 1.00, places=2)
assert almost_equal(cheeger(P, 5), 0.80, places=2)
def test_directed(self):
"""Tests the node boundary of a directed graph."""
G = nx.DiGraph([(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)])
S = {0, 1}
boundary = nx.node_boundary(G, S)
expected = {2}
assert boundary == expected
def test_multigraph(self):
"""Tests the node boundary of a multigraph."""
G = nx.MultiGraph(list(nx.cycle_graph(5).edges()) * 2)
S = {0, 1}
boundary = nx.node_boundary(G, S)
expected = {2, 4}
assert boundary == expected
def test_multidigraph(self):
"""Tests the edge boundary of a multdiigraph."""
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]
G = nx.MultiDiGraph(edges * 2)
S = {0, 1}
boundary = nx.node_boundary(G, S)
expected = {2}
assert boundary == expected
class TestEdgeBoundary:
"""Unit tests for the :func:`~networkx.edge_boundary` function."""
def test_null_graph(self):
null = nx.null_graph()
assert list(nx.edge_boundary(null, [])) == []
assert list(nx.edge_boundary(null, [], [])) == []
assert list(nx.edge_boundary(null, [1, 2, 3])) == []
assert list(nx.edge_boundary(null, [1, 2, 3], [4, 5, 6])) == []
assert list(nx.edge_boundary(null, [1, 2, 3], [3, 4, 5])) == []
def test_path_graph(self):
P10 = cnlti(nx.path_graph(10), first_label=1)
assert list(nx.edge_boundary(P10, [])) == []
assert list(nx.edge_boundary(P10, [], [])) == []
assert list(nx.edge_boundary(P10, [1, 2, 3])) == [(3, 4)]
assert sorted(nx.edge_boundary(P10, [4, 5, 6])) == [(4, 3), (6, 7)]
assert sorted(nx.edge_boundary(P10, [3, 4, 5, 6, 7])) == [(3, 2), (7, 8)]
assert list(nx.edge_boundary(P10, [8, 9, 10])) == [(8, 7)]
assert sorted(nx.edge_boundary(P10, [4, 5, 6], [9, 10])) == []
assert list(nx.edge_boundary(P10, [1, 2, 3], [3, 4, 5])) == [(2, 3), (3, 4)]
def test_complete_graph(self):
K10 = cnlti(nx.complete_graph(10), first_label=1)
def ilen(iterable):
return sum(1 for i in iterable)
assert list(nx.edge_boundary(K10, [])) == []
assert list(nx.edge_boundary(K10, [], [])) == []
assert ilen(nx.edge_boundary(K10, [1, 2, 3])) == 21
assert ilen(nx.edge_boundary(K10, [4, 5, 6, 7])) == 24
assert ilen(nx.edge_boundary(K10, [3, 4, 5, 6, 7])) == 25
assert ilen(nx.edge_boundary(K10, [8, 9, 10])) == 21
assert_edges_equal(
nx.edge_boundary(K10, [4, 5, 6], [9, 10]),
[(4, 9), (4, 10), (5, 9), (5, 10), (6, 9), (6, 10)],
)
assert_edges_equal(
nx.edge_boundary(K10, [1, 2, 3], [3, 4, 5]),
[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5)],
)
def test_directed(self):
"""Tests the edge boundary of a directed graph."""
G = nx.DiGraph([(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)])
S = {0, 1}
boundary = list(nx.edge_boundary(G, S))
expected = [(1, 2)]
assert boundary == expected
def test_multigraph(self):
"""Tests the edge boundary of a multigraph."""
G = nx.MultiGraph(list(nx.cycle_graph(5).edges()) * 2)
S = {0, 1}
boundary = list(nx.edge_boundary(G, S))
expected = [(0, 4), (0, 4), (1, 2), (1, 2)]
assert boundary == expected
def test_multidigraph(self):
"""Tests the edge boundary of a multdiigraph."""
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]
G = nx.MultiDiGraph(edges * 2)
S = {0, 1}
boundary = list(nx.edge_boundary(G, S))
expected = [(1, 2), (1, 2)]
assert boundary == expected

View file

@ -0,0 +1,74 @@
"""Unit tests for bridge-finding algorithms."""
import networkx as nx
class TestBridges:
"""Unit tests for the bridge-finding function."""
def test_single_bridge(self):
edges = [
# DFS tree edges.
(1, 2),
(2, 3),
(3, 4),
(3, 5),
(5, 6),
(6, 7),
(7, 8),
(5, 9),
(9, 10),
# Nontree edges.
(1, 3),
(1, 4),
(2, 5),
(5, 10),
(6, 8),
]
G = nx.Graph(edges)
source = 1
bridges = list(nx.bridges(G, source))
assert bridges == [(5, 6)]
def test_barbell_graph(self):
# The (3, 0) barbell graph has two triangles joined by a single edge.
G = nx.barbell_graph(3, 0)
source = 0
bridges = list(nx.bridges(G, source))
assert bridges == [(2, 3)]
class TestLocalBridges:
"""Unit tests for the local_bridge function."""
@classmethod
def setup_class(cls):
cls.BB = nx.barbell_graph(4, 0)
cls.square = nx.cycle_graph(4)
cls.tri = nx.cycle_graph(3)
def test_nospan(self):
expected = {(3, 4), (4, 3)}
assert next(nx.local_bridges(self.BB, with_span=False)) in expected
assert set(nx.local_bridges(self.square, with_span=False)) == self.square.edges
assert list(nx.local_bridges(self.tri, with_span=False)) == []
def test_no_weight(self):
inf = float("inf")
expected = {(3, 4, inf), (4, 3, inf)}
assert next(nx.local_bridges(self.BB)) in expected
expected = {(u, v, 3) for u, v, in self.square.edges}
assert set(nx.local_bridges(self.square)) == expected
assert list(nx.local_bridges(self.tri)) == []
def test_weight(self):
inf = float("inf")
G = self.square.copy()
G.edges[1, 2]["weight"] = 2
expected = {(u, v, 5 - wt) for u, v, wt in G.edges(data="weight", default=1)}
assert set(nx.local_bridges(G, weight="weight")) == expected
expected = {(u, v, 6) for u, v in G.edges}
lb = nx.local_bridges(G, weight=lambda u, v, d: 2)
assert set(lb) == expected

View file

@ -0,0 +1,132 @@
"""Unit tests for the chain decomposition functions."""
from itertools import cycle
from itertools import islice
import networkx as nx
def cycles(seq):
"""Yields cyclic permutations of the given sequence.
For example::
>>> list(cycles("abc"))
[('a', 'b', 'c'), ('b', 'c', 'a'), ('c', 'a', 'b')]
"""
n = len(seq)
cycled_seq = cycle(seq)
for x in seq:
yield tuple(islice(cycled_seq, n))
next(cycled_seq)
def cyclic_equals(seq1, seq2):
"""Decide whether two sequences are equal up to cyclic permutations.
For example::
>>> cyclic_equals("xyz", "zxy")
True
>>> cyclic_equals("xyz", "zyx")
False
"""
# Cast seq2 to a tuple since `cycles()` yields tuples.
seq2 = tuple(seq2)
return any(x == tuple(seq2) for x in cycles(seq1))
class TestChainDecomposition:
"""Unit tests for the chain decomposition function."""
def assertContainsChain(self, chain, expected):
# A cycle could be expressed in two different orientations, one
# forward and one backward, so we need to check for cyclic
# equality in both orientations.
reversed_chain = list(reversed([tuple(reversed(e)) for e in chain]))
for candidate in expected:
if cyclic_equals(chain, candidate):
break
if cyclic_equals(reversed_chain, candidate):
break
else:
self.fail("chain not found")
def test_decomposition(self):
edges = [
# DFS tree edges.
(1, 2),
(2, 3),
(3, 4),
(3, 5),
(5, 6),
(6, 7),
(7, 8),
(5, 9),
(9, 10),
# Nontree edges.
(1, 3),
(1, 4),
(2, 5),
(5, 10),
(6, 8),
]
G = nx.Graph(edges)
expected = [
[(1, 3), (3, 2), (2, 1)],
[(1, 4), (4, 3)],
[(2, 5), (5, 3)],
[(5, 10), (10, 9), (9, 5)],
[(6, 8), (8, 7), (7, 6)],
]
chains = list(nx.chain_decomposition(G, root=1))
assert len(chains) == len(expected)
# This chain decomposition isn't unique
# for chain in chains:
# print(chain)
# self.assertContainsChain(chain, expected)
def test_barbell_graph(self):
# The (3, 0) barbell graph has two triangles joined by a single edge.
G = nx.barbell_graph(3, 0)
chains = list(nx.chain_decomposition(G, root=0))
expected = [[(0, 1), (1, 2), (2, 0)], [(3, 4), (4, 5), (5, 3)]]
assert len(chains) == len(expected)
for chain in chains:
self.assertContainsChain(chain, expected)
def test_disconnected_graph(self):
"""Test for a graph with multiple connected components."""
G = nx.barbell_graph(3, 0)
H = nx.barbell_graph(3, 0)
mapping = dict(zip(range(6), "abcdef"))
nx.relabel_nodes(H, mapping, copy=False)
G = nx.union(G, H)
chains = list(nx.chain_decomposition(G))
expected = [
[(0, 1), (1, 2), (2, 0)],
[(3, 4), (4, 5), (5, 3)],
[("a", "b"), ("b", "c"), ("c", "a")],
[("d", "e"), ("e", "f"), ("f", "d")],
]
assert len(chains) == len(expected)
for chain in chains:
self.assertContainsChain(chain, expected)
def test_disconnected_graph_root_node(self):
"""Test for a single component of a disconnected graph."""
G = nx.barbell_graph(3, 0)
H = nx.barbell_graph(3, 0)
mapping = dict(zip(range(6), "abcdef"))
nx.relabel_nodes(H, mapping, copy=False)
G = nx.union(G, H)
chains = list(nx.chain_decomposition(G, root="a"))
expected = [
[("a", "b"), ("b", "c"), ("c", "a")],
[("d", "e"), ("e", "f"), ("f", "d")],
]
assert len(chains) == len(expected)
for chain in chains:
self.assertContainsChain(chain, expected)

View file

@ -0,0 +1,109 @@
import pytest
import networkx as nx
class TestMCS:
@classmethod
def setup_class(cls):
# simple graph
connected_chordal_G = nx.Graph()
connected_chordal_G.add_edges_from(
[
(1, 2),
(1, 3),
(2, 3),
(2, 4),
(3, 4),
(3, 5),
(3, 6),
(4, 5),
(4, 6),
(5, 6),
]
)
cls.connected_chordal_G = connected_chordal_G
chordal_G = nx.Graph()
chordal_G.add_edges_from(
[
(1, 2),
(1, 3),
(2, 3),
(2, 4),
(3, 4),
(3, 5),
(3, 6),
(4, 5),
(4, 6),
(5, 6),
(7, 8),
]
)
chordal_G.add_node(9)
cls.chordal_G = chordal_G
non_chordal_G = nx.Graph()
non_chordal_G.add_edges_from([(1, 2), (1, 3), (2, 4), (2, 5), (3, 4), (3, 5)])
cls.non_chordal_G = non_chordal_G
def test_is_chordal(self):
assert not nx.is_chordal(self.non_chordal_G)
assert nx.is_chordal(self.chordal_G)
assert nx.is_chordal(self.connected_chordal_G)
assert nx.is_chordal(nx.complete_graph(3))
assert nx.is_chordal(nx.cycle_graph(3))
assert not nx.is_chordal(nx.cycle_graph(5))
def test_induced_nodes(self):
G = nx.generators.classic.path_graph(10)
Induced_nodes = nx.find_induced_nodes(G, 1, 9, 2)
assert Induced_nodes == {1, 2, 3, 4, 5, 6, 7, 8, 9}
pytest.raises(
nx.NetworkXTreewidthBoundExceeded, nx.find_induced_nodes, G, 1, 9, 1
)
Induced_nodes = nx.find_induced_nodes(self.chordal_G, 1, 6)
assert Induced_nodes == {1, 2, 4, 6}
pytest.raises(nx.NetworkXError, nx.find_induced_nodes, self.non_chordal_G, 1, 5)
def test_chordal_find_cliques(self):
cliques = {
frozenset([9]),
frozenset([7, 8]),
frozenset([1, 2, 3]),
frozenset([2, 3, 4]),
frozenset([3, 4, 5, 6]),
}
assert nx.chordal_graph_cliques(self.chordal_G) == cliques
def test_chordal_find_cliques_path(self):
G = nx.path_graph(10)
cliqueset = nx.chordal_graph_cliques(G)
for (u, v) in G.edges():
assert frozenset([u, v]) in cliqueset or frozenset([v, u]) in cliqueset
def test_chordal_find_cliquesCC(self):
cliques = {frozenset([1, 2, 3]), frozenset([2, 3, 4]), frozenset([3, 4, 5, 6])}
cgc = nx.chordal_graph_cliques
assert cgc(self.connected_chordal_G) == cliques
def test_complete_to_chordal_graph(self):
fgrg = nx.fast_gnp_random_graph
test_graphs = [
nx.barbell_graph(6, 2),
nx.cycle_graph(15),
nx.wheel_graph(20),
nx.grid_graph([10, 4]),
nx.ladder_graph(15),
nx.star_graph(5),
nx.bull_graph(),
fgrg(20, 0.3, seed=1),
]
for G in test_graphs:
H, a = nx.complete_to_chordal_graph(G)
assert nx.is_chordal(H)
assert len(a) == H.number_of_nodes()
if nx.is_chordal(G):
assert G.number_of_edges() == H.number_of_edges()
assert set(a.values()) == {0}
else:
assert len(set(a.values())) == H.number_of_nodes()

View file

@ -0,0 +1,280 @@
import pytest
import networkx as nx
from networkx import convert_node_labels_to_integers as cnlti
class TestCliques:
def setup_method(self):
z = [3, 4, 3, 4, 2, 4, 2, 1, 1, 1, 1]
self.G = cnlti(nx.generators.havel_hakimi_graph(z), first_label=1)
self.cl = list(nx.find_cliques(self.G))
H = nx.complete_graph(6)
H = nx.relabel_nodes(H, {i: i + 1 for i in range(6)})
H.remove_edges_from([(2, 6), (2, 5), (2, 4), (1, 3), (5, 3)])
self.H = H
def test_find_cliques1(self):
cl = list(nx.find_cliques(self.G))
rcl = nx.find_cliques_recursive(self.G)
expected = [[2, 6, 1, 3], [2, 6, 4], [5, 4, 7], [8, 9], [10, 11]]
assert sorted(map(sorted, cl)) == sorted(map(sorted, rcl))
assert sorted(map(sorted, cl)) == sorted(map(sorted, expected))
def test_selfloops(self):
self.G.add_edge(1, 1)
cl = list(nx.find_cliques(self.G))
rcl = list(nx.find_cliques_recursive(self.G))
assert set(map(frozenset, cl)) == set(map(frozenset, rcl))
answer = [{2, 6, 1, 3}, {2, 6, 4}, {5, 4, 7}, {8, 9}, {10, 11}]
assert len(answer) == len(cl)
assert all(set(c) in answer for c in cl)
def test_find_cliques2(self):
hcl = list(nx.find_cliques(self.H))
assert sorted(map(sorted, hcl)) == [[1, 2], [1, 4, 5, 6], [2, 3], [3, 4, 6]]
def test_clique_number(self):
G = self.G
assert nx.graph_clique_number(G) == 4
assert nx.graph_clique_number(G, cliques=self.cl) == 4
def test_clique_number2(self):
G = nx.Graph()
G.add_nodes_from([1, 2, 3])
assert nx.graph_clique_number(G) == 1
def test_clique_number3(self):
G = nx.Graph()
assert nx.graph_clique_number(G) == 0
def test_number_of_cliques(self):
G = self.G
assert nx.graph_number_of_cliques(G) == 5
assert nx.graph_number_of_cliques(G, cliques=self.cl) == 5
assert nx.number_of_cliques(G, 1) == 1
assert list(nx.number_of_cliques(G, [1]).values()) == [1]
assert list(nx.number_of_cliques(G, [1, 2]).values()) == [1, 2]
assert nx.number_of_cliques(G, [1, 2]) == {1: 1, 2: 2}
assert nx.number_of_cliques(G, 2) == 2
assert nx.number_of_cliques(G) == {
1: 1,
2: 2,
3: 1,
4: 2,
5: 1,
6: 2,
7: 1,
8: 1,
9: 1,
10: 1,
11: 1,
}
assert nx.number_of_cliques(G, nodes=list(G)) == {
1: 1,
2: 2,
3: 1,
4: 2,
5: 1,
6: 2,
7: 1,
8: 1,
9: 1,
10: 1,
11: 1,
}
assert nx.number_of_cliques(G, nodes=[2, 3, 4]) == {2: 2, 3: 1, 4: 2}
assert nx.number_of_cliques(G, cliques=self.cl) == {
1: 1,
2: 2,
3: 1,
4: 2,
5: 1,
6: 2,
7: 1,
8: 1,
9: 1,
10: 1,
11: 1,
}
assert nx.number_of_cliques(G, list(G), cliques=self.cl) == {
1: 1,
2: 2,
3: 1,
4: 2,
5: 1,
6: 2,
7: 1,
8: 1,
9: 1,
10: 1,
11: 1,
}
def test_node_clique_number(self):
G = self.G
assert nx.node_clique_number(G, 1) == 4
assert list(nx.node_clique_number(G, [1]).values()) == [4]
assert list(nx.node_clique_number(G, [1, 2]).values()) == [4, 4]
assert nx.node_clique_number(G, [1, 2]) == {1: 4, 2: 4}
assert nx.node_clique_number(G, 1) == 4
assert nx.node_clique_number(G) == {
1: 4,
2: 4,
3: 4,
4: 3,
5: 3,
6: 4,
7: 3,
8: 2,
9: 2,
10: 2,
11: 2,
}
assert nx.node_clique_number(G, cliques=self.cl) == {
1: 4,
2: 4,
3: 4,
4: 3,
5: 3,
6: 4,
7: 3,
8: 2,
9: 2,
10: 2,
11: 2,
}
def test_cliques_containing_node(self):
G = self.G
assert nx.cliques_containing_node(G, 1) == [[2, 6, 1, 3]]
assert list(nx.cliques_containing_node(G, [1]).values()) == [[[2, 6, 1, 3]]]
assert [
sorted(c) for c in list(nx.cliques_containing_node(G, [1, 2]).values())
] == [[[2, 6, 1, 3]], [[2, 6, 1, 3], [2, 6, 4]]]
result = nx.cliques_containing_node(G, [1, 2])
for k, v in result.items():
result[k] = sorted(v)
assert result == {1: [[2, 6, 1, 3]], 2: [[2, 6, 1, 3], [2, 6, 4]]}
assert nx.cliques_containing_node(G, 1) == [[2, 6, 1, 3]]
expected = [{2, 6, 1, 3}, {2, 6, 4}]
answer = [set(c) for c in nx.cliques_containing_node(G, 2)]
assert answer in (expected, list(reversed(expected)))
answer = [set(c) for c in nx.cliques_containing_node(G, 2, cliques=self.cl)]
assert answer in (expected, list(reversed(expected)))
assert len(nx.cliques_containing_node(G)) == 11
def test_make_clique_bipartite(self):
G = self.G
B = nx.make_clique_bipartite(G)
assert sorted(B) == [-5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# Project onto the nodes of the original graph.
H = nx.project(B, range(1, 12))
assert H.adj == G.adj
# Project onto the nodes representing the cliques.
H1 = nx.project(B, range(-5, 0))
# Relabel the negative numbers as positive ones.
H1 = nx.relabel_nodes(H1, {-v: v for v in range(1, 6)})
assert sorted(H1) == [1, 2, 3, 4, 5]
def test_make_max_clique_graph(self):
"""Tests that the maximal clique graph is the same as the bipartite
clique graph after being projected onto the nodes representing the
cliques.
"""
G = self.G
B = nx.make_clique_bipartite(G)
# Project onto the nodes representing the cliques.
H1 = nx.project(B, range(-5, 0))
# Relabel the negative numbers as nonnegative ones, starting at
# 0.
H1 = nx.relabel_nodes(H1, {-v: v - 1 for v in range(1, 6)})
H2 = nx.make_max_clique_graph(G)
assert H1.adj == H2.adj
def test_directed(self):
with pytest.raises(nx.NetworkXNotImplemented):
cliques = nx.find_cliques(nx.DiGraph())
class TestEnumerateAllCliques:
def test_paper_figure_4(self):
# Same graph as given in Fig. 4 of paper enumerate_all_cliques is
# based on.
# http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=1559964&isnumber=33129
G = nx.Graph()
edges_fig_4 = [
("a", "b"),
("a", "c"),
("a", "d"),
("a", "e"),
("b", "c"),
("b", "d"),
("b", "e"),
("c", "d"),
("c", "e"),
("d", "e"),
("f", "b"),
("f", "c"),
("f", "g"),
("g", "f"),
("g", "c"),
("g", "d"),
("g", "e"),
]
G.add_edges_from(edges_fig_4)
cliques = list(nx.enumerate_all_cliques(G))
clique_sizes = list(map(len, cliques))
assert sorted(clique_sizes) == clique_sizes
expected_cliques = [
["a"],
["b"],
["c"],
["d"],
["e"],
["f"],
["g"],
["a", "b"],
["a", "b", "d"],
["a", "b", "d", "e"],
["a", "b", "e"],
["a", "c"],
["a", "c", "d"],
["a", "c", "d", "e"],
["a", "c", "e"],
["a", "d"],
["a", "d", "e"],
["a", "e"],
["b", "c"],
["b", "c", "d"],
["b", "c", "d", "e"],
["b", "c", "e"],
["b", "c", "f"],
["b", "d"],
["b", "d", "e"],
["b", "e"],
["b", "f"],
["c", "d"],
["c", "d", "e"],
["c", "d", "e", "g"],
["c", "d", "g"],
["c", "e"],
["c", "e", "g"],
["c", "f"],
["c", "f", "g"],
["c", "g"],
["d", "e"],
["d", "e", "g"],
["d", "g"],
["e", "g"],
["f", "g"],
["a", "b", "c"],
["a", "b", "c", "d"],
["a", "b", "c", "d", "e"],
["a", "b", "c", "e"],
]
assert sorted(map(sorted, cliques)) == sorted(map(sorted, expected_cliques))

View file

@ -0,0 +1,436 @@
import networkx as nx
class TestTriangles:
def test_empty(self):
G = nx.Graph()
assert list(nx.triangles(G).values()) == []
def test_path(self):
G = nx.path_graph(10)
assert list(nx.triangles(G).values()) == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
assert nx.triangles(G) == {
0: 0,
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
6: 0,
7: 0,
8: 0,
9: 0,
}
def test_cubical(self):
G = nx.cubical_graph()
assert list(nx.triangles(G).values()) == [0, 0, 0, 0, 0, 0, 0, 0]
assert nx.triangles(G, 1) == 0
assert list(nx.triangles(G, [1, 2]).values()) == [0, 0]
assert nx.triangles(G, 1) == 0
assert nx.triangles(G, [1, 2]) == {1: 0, 2: 0}
def test_k5(self):
G = nx.complete_graph(5)
assert list(nx.triangles(G).values()) == [6, 6, 6, 6, 6]
assert sum(nx.triangles(G).values()) / 3.0 == 10
assert nx.triangles(G, 1) == 6
G.remove_edge(1, 2)
assert list(nx.triangles(G).values()) == [5, 3, 3, 5, 5]
assert nx.triangles(G, 1) == 3
class TestDirectedClustering:
def test_clustering(self):
G = nx.DiGraph()
assert list(nx.clustering(G).values()) == []
assert nx.clustering(G) == {}
def test_path(self):
G = nx.path_graph(10, create_using=nx.DiGraph())
assert list(nx.clustering(G).values()) == [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
]
assert nx.clustering(G) == {
0: 0.0,
1: 0.0,
2: 0.0,
3: 0.0,
4: 0.0,
5: 0.0,
6: 0.0,
7: 0.0,
8: 0.0,
9: 0.0,
}
def test_k5(self):
G = nx.complete_graph(5, create_using=nx.DiGraph())
assert list(nx.clustering(G).values()) == [1, 1, 1, 1, 1]
assert nx.average_clustering(G) == 1
G.remove_edge(1, 2)
assert list(nx.clustering(G).values()) == [
11.0 / 12.0,
1.0,
1.0,
11.0 / 12.0,
11.0 / 12.0,
]
assert nx.clustering(G, [1, 4]) == {1: 1.0, 4: 11.0 / 12.0}
G.remove_edge(2, 1)
assert list(nx.clustering(G).values()) == [
5.0 / 6.0,
1.0,
1.0,
5.0 / 6.0,
5.0 / 6.0,
]
assert nx.clustering(G, [1, 4]) == {1: 1.0, 4: 0.83333333333333337}
def test_triangle_and_edge(self):
G = nx.cycle_graph(3, create_using=nx.DiGraph())
G.add_edge(0, 4)
assert nx.clustering(G)[0] == 1.0 / 6.0
class TestDirectedWeightedClustering:
def test_clustering(self):
G = nx.DiGraph()
assert list(nx.clustering(G, weight="weight").values()) == []
assert nx.clustering(G) == {}
def test_path(self):
G = nx.path_graph(10, create_using=nx.DiGraph())
assert list(nx.clustering(G, weight="weight").values()) == [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
]
assert nx.clustering(G, weight="weight") == {
0: 0.0,
1: 0.0,
2: 0.0,
3: 0.0,
4: 0.0,
5: 0.0,
6: 0.0,
7: 0.0,
8: 0.0,
9: 0.0,
}
def test_k5(self):
G = nx.complete_graph(5, create_using=nx.DiGraph())
assert list(nx.clustering(G, weight="weight").values()) == [1, 1, 1, 1, 1]
assert nx.average_clustering(G, weight="weight") == 1
G.remove_edge(1, 2)
assert list(nx.clustering(G, weight="weight").values()) == [
11.0 / 12.0,
1.0,
1.0,
11.0 / 12.0,
11.0 / 12.0,
]
assert nx.clustering(G, [1, 4], weight="weight") == {1: 1.0, 4: 11.0 / 12.0}
G.remove_edge(2, 1)
assert list(nx.clustering(G, weight="weight").values()) == [
5.0 / 6.0,
1.0,
1.0,
5.0 / 6.0,
5.0 / 6.0,
]
assert nx.clustering(G, [1, 4], weight="weight") == {
1: 1.0,
4: 0.83333333333333337,
}
def test_triangle_and_edge(self):
G = nx.cycle_graph(3, create_using=nx.DiGraph())
G.add_edge(0, 4, weight=2)
assert nx.clustering(G)[0] == 1.0 / 6.0
assert nx.clustering(G, weight="weight")[0] == 1.0 / 12.0
class TestWeightedClustering:
def test_clustering(self):
G = nx.Graph()
assert list(nx.clustering(G, weight="weight").values()) == []
assert nx.clustering(G) == {}
def test_path(self):
G = nx.path_graph(10)
assert list(nx.clustering(G, weight="weight").values()) == [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
]
assert nx.clustering(G, weight="weight") == {
0: 0.0,
1: 0.0,
2: 0.0,
3: 0.0,
4: 0.0,
5: 0.0,
6: 0.0,
7: 0.0,
8: 0.0,
9: 0.0,
}
def test_cubical(self):
G = nx.cubical_graph()
assert list(nx.clustering(G, weight="weight").values()) == [
0,
0,
0,
0,
0,
0,
0,
0,
]
assert nx.clustering(G, 1) == 0
assert list(nx.clustering(G, [1, 2], weight="weight").values()) == [0, 0]
assert nx.clustering(G, 1, weight="weight") == 0
assert nx.clustering(G, [1, 2], weight="weight") == {1: 0, 2: 0}
def test_k5(self):
G = nx.complete_graph(5)
assert list(nx.clustering(G, weight="weight").values()) == [1, 1, 1, 1, 1]
assert nx.average_clustering(G, weight="weight") == 1
G.remove_edge(1, 2)
assert list(nx.clustering(G, weight="weight").values()) == [
5.0 / 6.0,
1.0,
1.0,
5.0 / 6.0,
5.0 / 6.0,
]
assert nx.clustering(G, [1, 4], weight="weight") == {
1: 1.0,
4: 0.83333333333333337,
}
def test_triangle_and_edge(self):
G = nx.cycle_graph(3)
G.add_edge(0, 4, weight=2)
assert nx.clustering(G)[0] == 1.0 / 3.0
assert nx.clustering(G, weight="weight")[0] == 1.0 / 6.0
class TestClustering:
def test_clustering(self):
G = nx.Graph()
assert list(nx.clustering(G).values()) == []
assert nx.clustering(G) == {}
def test_path(self):
G = nx.path_graph(10)
assert list(nx.clustering(G).values()) == [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
]
assert nx.clustering(G) == {
0: 0.0,
1: 0.0,
2: 0.0,
3: 0.0,
4: 0.0,
5: 0.0,
6: 0.0,
7: 0.0,
8: 0.0,
9: 0.0,
}
def test_cubical(self):
G = nx.cubical_graph()
assert list(nx.clustering(G).values()) == [0, 0, 0, 0, 0, 0, 0, 0]
assert nx.clustering(G, 1) == 0
assert list(nx.clustering(G, [1, 2]).values()) == [0, 0]
assert nx.clustering(G, 1) == 0
assert nx.clustering(G, [1, 2]) == {1: 0, 2: 0}
def test_k5(self):
G = nx.complete_graph(5)
assert list(nx.clustering(G).values()) == [1, 1, 1, 1, 1]
assert nx.average_clustering(G) == 1
G.remove_edge(1, 2)
assert list(nx.clustering(G).values()) == [
5.0 / 6.0,
1.0,
1.0,
5.0 / 6.0,
5.0 / 6.0,
]
assert nx.clustering(G, [1, 4]) == {1: 1.0, 4: 0.83333333333333337}
class TestTransitivity:
def test_transitivity(self):
G = nx.Graph()
assert nx.transitivity(G) == 0.0
def test_path(self):
G = nx.path_graph(10)
assert nx.transitivity(G) == 0.0
def test_cubical(self):
G = nx.cubical_graph()
assert nx.transitivity(G) == 0.0
def test_k5(self):
G = nx.complete_graph(5)
assert nx.transitivity(G) == 1.0
G.remove_edge(1, 2)
assert nx.transitivity(G) == 0.875
class TestSquareClustering:
def test_clustering(self):
G = nx.Graph()
assert list(nx.square_clustering(G).values()) == []
assert nx.square_clustering(G) == {}
def test_path(self):
G = nx.path_graph(10)
assert list(nx.square_clustering(G).values()) == [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
]
assert nx.square_clustering(G) == {
0: 0.0,
1: 0.0,
2: 0.0,
3: 0.0,
4: 0.0,
5: 0.0,
6: 0.0,
7: 0.0,
8: 0.0,
9: 0.0,
}
def test_cubical(self):
G = nx.cubical_graph()
assert list(nx.square_clustering(G).values()) == [
0.5,
0.5,
0.5,
0.5,
0.5,
0.5,
0.5,
0.5,
]
assert list(nx.square_clustering(G, [1, 2]).values()) == [0.5, 0.5]
assert nx.square_clustering(G, [1])[1] == 0.5
assert nx.square_clustering(G, [1, 2]) == {1: 0.5, 2: 0.5}
def test_k5(self):
G = nx.complete_graph(5)
assert list(nx.square_clustering(G).values()) == [1, 1, 1, 1, 1]
def test_bipartite_k5(self):
G = nx.complete_bipartite_graph(5, 5)
assert list(nx.square_clustering(G).values()) == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
def test_lind_square_clustering(self):
"""Test C4 for figure 1 Lind et al (2005)"""
G = nx.Graph(
[
(1, 2),
(1, 3),
(1, 6),
(1, 7),
(2, 4),
(2, 5),
(3, 4),
(3, 5),
(6, 7),
(7, 8),
(6, 8),
(7, 9),
(7, 10),
(6, 11),
(6, 12),
(2, 13),
(2, 14),
(3, 15),
(3, 16),
]
)
G1 = G.subgraph([1, 2, 3, 4, 5, 13, 14, 15, 16])
G2 = G.subgraph([1, 6, 7, 8, 9, 10, 11, 12])
assert nx.square_clustering(G, [1])[1] == 3 / 75.0
assert nx.square_clustering(G1, [1])[1] == 2 / 6.0
assert nx.square_clustering(G2, [1])[1] == 1 / 5.0
def test_average_clustering():
G = nx.cycle_graph(3)
G.add_edge(2, 3)
assert nx.average_clustering(G) == (1 + 1 + 1 / 3.0) / 4.0
assert nx.average_clustering(G, count_zeros=True) == (1 + 1 + 1 / 3.0) / 4.0
assert nx.average_clustering(G, count_zeros=False) == (1 + 1 + 1 / 3.0) / 3.0
class TestGeneralizedDegree:
def test_generalized_degree(self):
G = nx.Graph()
assert nx.generalized_degree(G) == {}
def test_path(self):
G = nx.path_graph(5)
assert nx.generalized_degree(G, 0) == {0: 1}
assert nx.generalized_degree(G, 1) == {0: 2}
def test_cubical(self):
G = nx.cubical_graph()
assert nx.generalized_degree(G, 0) == {0: 3}
def test_k5(self):
G = nx.complete_graph(5)
assert nx.generalized_degree(G, 0) == {3: 4}
G.remove_edge(0, 1)
assert nx.generalized_degree(G, 0) == {2: 3}

View file

@ -0,0 +1,82 @@
from collections import defaultdict
import pytest
numpy = pytest.importorskip("numpy")
scipy = pytest.importorskip("scipy")
import networkx as nx
from networkx.testing import almost_equal
from networkx.algorithms.communicability_alg import communicability, communicability_exp
class TestCommunicability:
def test_communicability(self):
answer = {
0: {0: 1.5430806348152435, 1: 1.1752011936438012},
1: {0: 1.1752011936438012, 1: 1.5430806348152435},
}
# answer={(0, 0): 1.5430806348152435,
# (0, 1): 1.1752011936438012,
# (1, 0): 1.1752011936438012,
# (1, 1): 1.5430806348152435}
result = communicability(nx.path_graph(2))
for k1, val in result.items():
for k2 in val:
assert almost_equal(answer[k1][k2], result[k1][k2], places=7)
def test_communicability2(self):
answer_orig = {
("1", "1"): 1.6445956054135658,
("1", "Albert"): 0.7430186221096251,
("1", "Aric"): 0.7430186221096251,
("1", "Dan"): 1.6208126320442937,
("1", "Franck"): 0.42639707170035257,
("Albert", "1"): 0.7430186221096251,
("Albert", "Albert"): 2.4368257358712189,
("Albert", "Aric"): 1.4368257358712191,
("Albert", "Dan"): 2.0472097037446453,
("Albert", "Franck"): 1.8340111678944691,
("Aric", "1"): 0.7430186221096251,
("Aric", "Albert"): 1.4368257358712191,
("Aric", "Aric"): 2.4368257358712193,
("Aric", "Dan"): 2.0472097037446457,
("Aric", "Franck"): 1.8340111678944691,
("Dan", "1"): 1.6208126320442937,
("Dan", "Albert"): 2.0472097037446453,
("Dan", "Aric"): 2.0472097037446457,
("Dan", "Dan"): 3.1306328496328168,
("Dan", "Franck"): 1.4860372442192515,
("Franck", "1"): 0.42639707170035257,
("Franck", "Albert"): 1.8340111678944691,
("Franck", "Aric"): 1.8340111678944691,
("Franck", "Dan"): 1.4860372442192515,
("Franck", "Franck"): 2.3876142275231915,
}
answer = defaultdict(dict)
for (k1, k2), v in answer_orig.items():
answer[k1][k2] = v
G1 = nx.Graph(
[
("Franck", "Aric"),
("Aric", "Dan"),
("Dan", "Albert"),
("Albert", "Franck"),
("Dan", "1"),
("Franck", "Albert"),
]
)
result = communicability(G1)
for k1, val in result.items():
for k2 in val:
assert almost_equal(answer[k1][k2], result[k1][k2], places=7)
result = communicability_exp(G1)
for k1, val in result.items():
for k2 in val:
assert almost_equal(answer[k1][k2], result[k1][k2], places=7)

View file

@ -0,0 +1,179 @@
import networkx as nx
from networkx.testing.utils import assert_nodes_equal
class TestCore:
@classmethod
def setup_class(cls):
# G is the example graph in Figure 1 from Batagelj and
# Zaversnik's paper titled An O(m) Algorithm for Cores
# Decomposition of Networks, 2003,
# http://arXiv.org/abs/cs/0310049. With nodes labeled as
# shown, the 3-core is given by nodes 1-8, the 2-core by nodes
# 9-16, the 1-core by nodes 17-20 and node 21 is in the
# 0-core.
t1 = nx.convert_node_labels_to_integers(nx.tetrahedral_graph(), 1)
t2 = nx.convert_node_labels_to_integers(t1, 5)
G = nx.union(t1, t2)
G.add_edges_from(
[
(3, 7),
(2, 11),
(11, 5),
(11, 12),
(5, 12),
(12, 19),
(12, 18),
(3, 9),
(7, 9),
(7, 10),
(9, 10),
(9, 20),
(17, 13),
(13, 14),
(14, 15),
(15, 16),
(16, 13),
]
)
G.add_node(21)
cls.G = G
# Create the graph H resulting from the degree sequence
# [0, 1, 2, 2, 2, 2, 3] when using the Havel-Hakimi algorithm.
degseq = [0, 1, 2, 2, 2, 2, 3]
H = nx.havel_hakimi_graph(degseq)
mapping = {6: 0, 0: 1, 4: 3, 5: 6, 3: 4, 1: 2, 2: 5}
cls.H = nx.relabel_nodes(H, mapping)
def test_trivial(self):
"""Empty graph"""
G = nx.Graph()
assert nx.find_cores(G) == {}
def test_find_cores(self):
core = nx.find_cores(self.G)
nodes_by_core = [
sorted([n for n in core if core[n] == val]) for val in range(4)
]
assert_nodes_equal(nodes_by_core[0], [21])
assert_nodes_equal(nodes_by_core[1], [17, 18, 19, 20])
assert_nodes_equal(nodes_by_core[2], [9, 10, 11, 12, 13, 14, 15, 16])
assert_nodes_equal(nodes_by_core[3], [1, 2, 3, 4, 5, 6, 7, 8])
def test_core_number(self):
# smoke test real name
cores = nx.core_number(self.G)
def test_find_cores2(self):
core = nx.find_cores(self.H)
nodes_by_core = [
sorted([n for n in core if core[n] == val]) for val in range(3)
]
assert_nodes_equal(nodes_by_core[0], [0])
assert_nodes_equal(nodes_by_core[1], [1, 3])
assert_nodes_equal(nodes_by_core[2], [2, 4, 5, 6])
def test_directed_find_cores(self):
"""core number had a bug for directed graphs found in issue #1959"""
# small example where too timid edge removal can make cn[2] = 3
G = nx.DiGraph()
edges = [(1, 2), (2, 1), (2, 3), (2, 4), (3, 4), (4, 3)]
G.add_edges_from(edges)
assert nx.core_number(G) == {1: 2, 2: 2, 3: 2, 4: 2}
# small example where too aggressive edge removal can make cn[2] = 2
more_edges = [(1, 5), (3, 5), (4, 5), (3, 6), (4, 6), (5, 6)]
G.add_edges_from(more_edges)
assert nx.core_number(G) == {1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3}
def test_main_core(self):
main_core_subgraph = nx.k_core(self.H)
assert sorted(main_core_subgraph.nodes()) == [2, 4, 5, 6]
def test_k_core(self):
# k=0
k_core_subgraph = nx.k_core(self.H, k=0)
assert sorted(k_core_subgraph.nodes()) == sorted(self.H.nodes())
# k=1
k_core_subgraph = nx.k_core(self.H, k=1)
assert sorted(k_core_subgraph.nodes()) == [1, 2, 3, 4, 5, 6]
# k = 2
k_core_subgraph = nx.k_core(self.H, k=2)
assert sorted(k_core_subgraph.nodes()) == [2, 4, 5, 6]
def test_main_crust(self):
main_crust_subgraph = nx.k_crust(self.H)
assert sorted(main_crust_subgraph.nodes()) == [0, 1, 3]
def test_k_crust(self):
# k = 0
k_crust_subgraph = nx.k_crust(self.H, k=2)
assert sorted(k_crust_subgraph.nodes()) == sorted(self.H.nodes())
# k=1
k_crust_subgraph = nx.k_crust(self.H, k=1)
assert sorted(k_crust_subgraph.nodes()) == [0, 1, 3]
# k=2
k_crust_subgraph = nx.k_crust(self.H, k=0)
assert sorted(k_crust_subgraph.nodes()) == [0]
def test_main_shell(self):
main_shell_subgraph = nx.k_shell(self.H)
assert sorted(main_shell_subgraph.nodes()) == [2, 4, 5, 6]
def test_k_shell(self):
# k=0
k_shell_subgraph = nx.k_shell(self.H, k=2)
assert sorted(k_shell_subgraph.nodes()) == [2, 4, 5, 6]
# k=1
k_shell_subgraph = nx.k_shell(self.H, k=1)
assert sorted(k_shell_subgraph.nodes()) == [1, 3]
# k=2
k_shell_subgraph = nx.k_shell(self.H, k=0)
assert sorted(k_shell_subgraph.nodes()) == [0]
def test_k_corona(self):
# k=0
k_corona_subgraph = nx.k_corona(self.H, k=2)
assert sorted(k_corona_subgraph.nodes()) == [2, 4, 5, 6]
# k=1
k_corona_subgraph = nx.k_corona(self.H, k=1)
assert sorted(k_corona_subgraph.nodes()) == [1]
# k=2
k_corona_subgraph = nx.k_corona(self.H, k=0)
assert sorted(k_corona_subgraph.nodes()) == [0]
def test_k_truss(self):
# k=-1
k_truss_subgraph = nx.k_truss(self.G, -1)
assert sorted(k_truss_subgraph.nodes()) == list(range(1, 21))
# k=0
k_truss_subgraph = nx.k_truss(self.G, 0)
assert sorted(k_truss_subgraph.nodes()) == list(range(1, 21))
# k=1
k_truss_subgraph = nx.k_truss(self.G, 1)
assert sorted(k_truss_subgraph.nodes()) == list(range(1, 21))
# k=2
k_truss_subgraph = nx.k_truss(self.G, 2)
assert sorted(k_truss_subgraph.nodes()) == list(range(1, 21))
# k=3
k_truss_subgraph = nx.k_truss(self.G, 3)
assert sorted(k_truss_subgraph.nodes()) == list(range(1, 13))
k_truss_subgraph = nx.k_truss(self.G, 4)
assert sorted(k_truss_subgraph.nodes()) == list(range(1, 9))
k_truss_subgraph = nx.k_truss(self.G, 5)
assert sorted(k_truss_subgraph.nodes()) == []
def test_onion_layers(self):
layers = nx.onion_layers(self.G)
nodes_by_layer = [
sorted([n for n in layers if layers[n] == val]) for val in range(1, 7)
]
assert_nodes_equal(nodes_by_layer[0], [21])
assert_nodes_equal(nodes_by_layer[1], [17, 18, 19, 20])
assert_nodes_equal(nodes_by_layer[2], [10, 12, 13, 14, 15, 16])
assert_nodes_equal(nodes_by_layer[3], [9, 11])
assert_nodes_equal(nodes_by_layer[4], [1, 2, 4, 5, 6, 8])
assert_nodes_equal(nodes_by_layer[5], [3, 7])

View file

@ -0,0 +1,58 @@
import networkx as nx
class TestMinEdgeCover:
"""Tests for :func:`networkx.algorithms.min_edge_cover`"""
def test_empty_graph(self):
G = nx.Graph()
assert nx.min_edge_cover(G) == set()
def test_graph_with_loop(self):
G = nx.Graph()
G.add_edge(0, 0)
assert nx.min_edge_cover(G) == {(0, 0)}
def test_graph_single_edge(self):
G = nx.Graph()
G.add_edge(0, 1)
assert nx.min_edge_cover(G) in ({(0, 1)}, {(1, 0)})
def test_bipartite_explicit(self):
G = nx.Graph()
G.add_nodes_from([1, 2, 3, 4], bipartite=0)
G.add_nodes_from(["a", "b", "c"], bipartite=1)
G.add_edges_from([(1, "a"), (1, "b"), (2, "b"), (2, "c"), (3, "c"), (4, "a")])
min_cover = nx.min_edge_cover(
G, nx.algorithms.bipartite.matching.eppstein_matching
)
min_cover2 = nx.min_edge_cover(G)
assert nx.is_edge_cover(G, min_cover)
assert len(min_cover) == 8
def test_complete_graph(self):
G = nx.complete_graph(10)
min_cover = nx.min_edge_cover(G)
assert nx.is_edge_cover(G, min_cover)
assert len(min_cover) == 5
class TestIsEdgeCover:
"""Tests for :func:`networkx.algorithms.is_edge_cover`"""
def test_empty_graph(self):
G = nx.Graph()
assert nx.is_edge_cover(G, set())
def test_graph_with_loop(self):
G = nx.Graph()
G.add_edge(1, 1)
assert nx.is_edge_cover(G, {(1, 1)})
def test_graph_single_edge(self):
G = nx.Graph()
G.add_edge(0, 1)
assert nx.is_edge_cover(G, {(0, 0), (1, 1)})
assert nx.is_edge_cover(G, {(0, 1), (1, 0)})
assert nx.is_edge_cover(G, {(0, 1)})
assert not nx.is_edge_cover(G, {(0, 0)})

View file

@ -0,0 +1,165 @@
"""Unit tests for the :mod:`networkx.algorithms.cuts` module."""
import networkx as nx
class TestCutSize:
"""Unit tests for the :func:`~networkx.cut_size` function."""
def test_symmetric(self):
"""Tests that the cut size is symmetric."""
G = nx.barbell_graph(3, 0)
S = {0, 1, 4}
T = {2, 3, 5}
assert nx.cut_size(G, S, T) == 4
assert nx.cut_size(G, T, S) == 4
def test_single_edge(self):
"""Tests for a cut of a single edge."""
G = nx.barbell_graph(3, 0)
S = {0, 1, 2}
T = {3, 4, 5}
assert nx.cut_size(G, S, T) == 1
assert nx.cut_size(G, T, S) == 1
def test_directed(self):
"""Tests that each directed edge is counted once in the cut."""
G = nx.barbell_graph(3, 0).to_directed()
S = {0, 1, 2}
T = {3, 4, 5}
assert nx.cut_size(G, S, T) == 2
assert nx.cut_size(G, T, S) == 2
def test_directed_symmetric(self):
"""Tests that a cut in a directed graph is symmetric."""
G = nx.barbell_graph(3, 0).to_directed()
S = {0, 1, 4}
T = {2, 3, 5}
assert nx.cut_size(G, S, T) == 8
assert nx.cut_size(G, T, S) == 8
def test_multigraph(self):
"""Tests that parallel edges are each counted for a cut."""
G = nx.MultiGraph(["ab", "ab"])
assert nx.cut_size(G, {"a"}, {"b"}) == 2
class TestVolume:
"""Unit tests for the :func:`~networkx.volume` function."""
def test_graph(self):
G = nx.cycle_graph(4)
assert nx.volume(G, {0, 1}) == 4
def test_digraph(self):
G = nx.DiGraph([(0, 1), (1, 2), (2, 3), (3, 0)])
assert nx.volume(G, {0, 1}) == 2
def test_multigraph(self):
edges = list(nx.cycle_graph(4).edges())
G = nx.MultiGraph(edges * 2)
assert nx.volume(G, {0, 1}) == 8
def test_multidigraph(self):
edges = [(0, 1), (1, 2), (2, 3), (3, 0)]
G = nx.MultiDiGraph(edges * 2)
assert nx.volume(G, {0, 1}) == 4
class TestNormalizedCutSize:
"""Unit tests for the :func:`~networkx.normalized_cut_size`
function.
"""
def test_graph(self):
G = nx.path_graph(4)
S = {1, 2}
T = set(G) - S
size = nx.normalized_cut_size(G, S, T)
# The cut looks like this: o-{-o--o-}-o
expected = 2 * ((1 / 4) + (1 / 2))
assert expected == size
def test_directed(self):
G = nx.DiGraph([(0, 1), (1, 2), (2, 3)])
S = {1, 2}
T = set(G) - S
size = nx.normalized_cut_size(G, S, T)
# The cut looks like this: o-{->o-->o-}->o
expected = 2 * ((1 / 2) + (1 / 1))
assert expected == size
class TestConductance:
"""Unit tests for the :func:`~networkx.conductance` function."""
def test_graph(self):
G = nx.barbell_graph(5, 0)
# Consider the singleton sets containing the "bridge" nodes.
# There is only one cut edge, and each set has volume five.
S = {4}
T = {5}
conductance = nx.conductance(G, S, T)
expected = 1 / 5
assert expected == conductance
class TestEdgeExpansion:
"""Unit tests for the :func:`~networkx.edge_expansion` function."""
def test_graph(self):
G = nx.barbell_graph(5, 0)
S = set(range(5))
T = set(G) - S
expansion = nx.edge_expansion(G, S, T)
expected = 1 / 5
assert expected == expansion
class TestNodeExpansion:
"""Unit tests for the :func:`~networkx.node_expansion` function.
"""
def test_graph(self):
G = nx.path_graph(8)
S = {3, 4, 5}
expansion = nx.node_expansion(G, S)
# The neighborhood of S has cardinality five, and S has
# cardinality three.
expected = 5 / 3
assert expected == expansion
class TestBoundaryExpansion:
"""Unit tests for the :func:`~networkx.boundary_expansion` function.
"""
def test_graph(self):
G = nx.complete_graph(10)
S = set(range(4))
expansion = nx.boundary_expansion(G, S)
# The node boundary of S has cardinality six, and S has
# cardinality three.
expected = 6 / 4
assert expected == expansion
class TestMixingExpansion:
"""Unit tests for the :func:`~networkx.mixing_expansion` function.
"""
def test_graph(self):
G = nx.barbell_graph(5, 0)
S = set(range(5))
T = set(G) - S
expansion = nx.mixing_expansion(G, S, T)
# There is one cut edge, and the total number of edges in the
# graph is twice the total number of edges in a clique of size
# five, plus one more for the bridge.
expected = 1 / (2 * (5 * 4 + 1))
assert expected == expansion

View file

@ -0,0 +1,350 @@
import pytest
import networkx
import networkx as nx
from networkx.algorithms import find_cycle
from networkx.algorithms import minimum_cycle_basis
FORWARD = nx.algorithms.edgedfs.FORWARD
REVERSE = nx.algorithms.edgedfs.REVERSE
class TestCycles:
@classmethod
def setup_class(cls):
G = networkx.Graph()
nx.add_cycle(G, [0, 1, 2, 3])
nx.add_cycle(G, [0, 3, 4, 5])
nx.add_cycle(G, [0, 1, 6, 7, 8])
G.add_edge(8, 9)
cls.G = G
def is_cyclic_permutation(self, a, b):
n = len(a)
if len(b) != n:
return False
l = a + a
return any(l[i : i + n] == b for i in range(n))
def test_cycle_basis(self):
G = self.G
cy = networkx.cycle_basis(G, 0)
sort_cy = sorted(sorted(c) for c in cy)
assert sort_cy == [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5]]
cy = networkx.cycle_basis(G, 1)
sort_cy = sorted(sorted(c) for c in cy)
assert sort_cy == [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5]]
cy = networkx.cycle_basis(G, 9)
sort_cy = sorted(sorted(c) for c in cy)
assert sort_cy == [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5]]
# test disconnected graphs
nx.add_cycle(G, "ABC")
cy = networkx.cycle_basis(G, 9)
sort_cy = sorted(sorted(c) for c in cy[:-1]) + [sorted(cy[-1])]
assert sort_cy == [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5], ["A", "B", "C"]]
def test_cycle_basis2(self):
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.DiGraph()
cy = networkx.cycle_basis(G, 0)
def test_cycle_basis3(self):
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.MultiGraph()
cy = networkx.cycle_basis(G, 0)
def test_simple_cycles(self):
edges = [(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)]
G = nx.DiGraph(edges)
cc = sorted(nx.simple_cycles(G))
ca = [[0], [0, 1, 2], [0, 2], [1, 2], [2]]
assert len(cc) == len(ca)
for c in cc:
assert any(self.is_cyclic_permutation(c, rc) for rc in ca)
def test_simple_cycles_graph(self):
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.Graph()
c = sorted(nx.simple_cycles(G))
def test_unsortable(self):
# TODO What does this test do? das 6/2013
G = nx.DiGraph()
nx.add_cycle(G, ["a", 1])
c = list(nx.simple_cycles(G))
def test_simple_cycles_small(self):
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3])
c = sorted(nx.simple_cycles(G))
assert len(c) == 1
assert self.is_cyclic_permutation(c[0], [1, 2, 3])
nx.add_cycle(G, [10, 20, 30])
cc = sorted(nx.simple_cycles(G))
assert len(cc) == 2
ca = [[1, 2, 3], [10, 20, 30]]
for c in cc:
assert any(self.is_cyclic_permutation(c, rc) for rc in ca)
def test_simple_cycles_empty(self):
G = nx.DiGraph()
assert list(nx.simple_cycles(G)) == []
def test_complete_directed_graph(self):
# see table 2 in Johnson's paper
ncircuits = [1, 5, 20, 84, 409, 2365, 16064]
for n, c in zip(range(2, 9), ncircuits):
G = nx.DiGraph(nx.complete_graph(n))
assert len(list(nx.simple_cycles(G))) == c
def worst_case_graph(self, k):
# see figure 1 in Johnson's paper
# this graph has exactly 3k simple cycles
G = nx.DiGraph()
for n in range(2, k + 2):
G.add_edge(1, n)
G.add_edge(n, k + 2)
G.add_edge(2 * k + 1, 1)
for n in range(k + 2, 2 * k + 2):
G.add_edge(n, 2 * k + 2)
G.add_edge(n, n + 1)
G.add_edge(2 * k + 3, k + 2)
for n in range(2 * k + 3, 3 * k + 3):
G.add_edge(2 * k + 2, n)
G.add_edge(n, 3 * k + 3)
G.add_edge(3 * k + 3, 2 * k + 2)
return G
def test_worst_case_graph(self):
# see figure 1 in Johnson's paper
for k in range(3, 10):
G = self.worst_case_graph(k)
l = len(list(nx.simple_cycles(G)))
assert l == 3 * k
def test_recursive_simple_and_not(self):
for k in range(2, 10):
G = self.worst_case_graph(k)
cc = sorted(nx.simple_cycles(G))
rcc = sorted(nx.recursive_simple_cycles(G))
assert len(cc) == len(rcc)
for c in cc:
assert any(self.is_cyclic_permutation(c, r) for r in rcc)
for rc in rcc:
assert any(self.is_cyclic_permutation(rc, c) for c in cc)
def test_simple_graph_with_reported_bug(self):
G = nx.DiGraph()
edges = [
(0, 2),
(0, 3),
(1, 0),
(1, 3),
(2, 1),
(2, 4),
(3, 2),
(3, 4),
(4, 0),
(4, 1),
(4, 5),
(5, 0),
(5, 1),
(5, 2),
(5, 3),
]
G.add_edges_from(edges)
cc = sorted(nx.simple_cycles(G))
assert len(cc) == 26
rcc = sorted(nx.recursive_simple_cycles(G))
assert len(cc) == len(rcc)
for c in cc:
assert any(self.is_cyclic_permutation(c, rc) for rc in rcc)
for rc in rcc:
assert any(self.is_cyclic_permutation(rc, c) for c in cc)
# These tests might fail with hash randomization since they depend on
# edge_dfs. For more information, see the comments in:
# networkx/algorithms/traversal/tests/test_edgedfs.py
class TestFindCycle:
@classmethod
def setup_class(cls):
cls.nodes = [0, 1, 2, 3]
cls.edges = [(-1, 0), (0, 1), (1, 0), (1, 0), (2, 1), (3, 1)]
def test_graph_nocycle(self):
G = nx.Graph(self.edges)
pytest.raises(nx.exception.NetworkXNoCycle, find_cycle, G, self.nodes)
def test_graph_cycle(self):
G = nx.Graph(self.edges)
G.add_edge(2, 0)
x = list(find_cycle(G, self.nodes))
x_ = [(0, 1), (1, 2), (2, 0)]
assert x == x_
def test_graph_orientation_none(self):
G = nx.Graph(self.edges)
G.add_edge(2, 0)
x = list(find_cycle(G, self.nodes, orientation=None))
x_ = [(0, 1), (1, 2), (2, 0)]
assert x == x_
def test_graph_orientation_original(self):
G = nx.Graph(self.edges)
G.add_edge(2, 0)
x = list(find_cycle(G, self.nodes, orientation="original"))
x_ = [(0, 1, FORWARD), (1, 2, FORWARD), (2, 0, FORWARD)]
assert x == x_
def test_digraph(self):
G = nx.DiGraph(self.edges)
x = list(find_cycle(G, self.nodes))
x_ = [(0, 1), (1, 0)]
assert x == x_
def test_digraph_orientation_none(self):
G = nx.DiGraph(self.edges)
x = list(find_cycle(G, self.nodes, orientation=None))
x_ = [(0, 1), (1, 0)]
assert x == x_
def test_digraph_orientation_original(self):
G = nx.DiGraph(self.edges)
x = list(find_cycle(G, self.nodes, orientation="original"))
x_ = [(0, 1, FORWARD), (1, 0, FORWARD)]
assert x == x_
def test_multigraph(self):
G = nx.MultiGraph(self.edges)
x = list(find_cycle(G, self.nodes))
x_ = [(0, 1, 0), (1, 0, 1)] # or (1, 0, 2)
# Hash randomization...could be any edge.
assert x[0] == x_[0]
assert x[1][:2] == x_[1][:2]
def test_multidigraph(self):
G = nx.MultiDiGraph(self.edges)
x = list(find_cycle(G, self.nodes))
x_ = [(0, 1, 0), (1, 0, 0)] # (1, 0, 1)
assert x[0] == x_[0]
assert x[1][:2] == x_[1][:2]
def test_digraph_ignore(self):
G = nx.DiGraph(self.edges)
x = list(find_cycle(G, self.nodes, orientation="ignore"))
x_ = [(0, 1, FORWARD), (1, 0, FORWARD)]
assert x == x_
def test_digraph_reverse(self):
G = nx.DiGraph(self.edges)
x = list(find_cycle(G, self.nodes, orientation="reverse"))
x_ = [(1, 0, REVERSE), (0, 1, REVERSE)]
assert x == x_
def test_multidigraph_ignore(self):
G = nx.MultiDiGraph(self.edges)
x = list(find_cycle(G, self.nodes, orientation="ignore"))
x_ = [(0, 1, 0, FORWARD), (1, 0, 0, FORWARD)] # or (1, 0, 1, 1)
assert x[0] == x_[0]
assert x[1][:2] == x_[1][:2]
assert x[1][3] == x_[1][3]
def test_multidigraph_ignore2(self):
# Loop traversed an edge while ignoring its orientation.
G = nx.MultiDiGraph([(0, 1), (1, 2), (1, 2)])
x = list(find_cycle(G, [0, 1, 2], orientation="ignore"))
x_ = [(1, 2, 0, FORWARD), (1, 2, 1, REVERSE)]
assert x == x_
def test_multidigraph_original(self):
# Node 2 doesn't need to be searched again from visited from 4.
# The goal here is to cover the case when 2 to be researched from 4,
# when 4 is visited from the first time (so we must make sure that 4
# is not visited from 2, and hence, we respect the edge orientation).
G = nx.MultiDiGraph([(0, 1), (1, 2), (2, 3), (4, 2)])
pytest.raises(
nx.exception.NetworkXNoCycle,
find_cycle,
G,
[0, 1, 2, 3, 4],
orientation="original",
)
def test_dag(self):
G = nx.DiGraph([(0, 1), (0, 2), (1, 2)])
pytest.raises(
nx.exception.NetworkXNoCycle, find_cycle, G, orientation="original"
)
x = list(find_cycle(G, orientation="ignore"))
assert x == [(0, 1, FORWARD), (1, 2, FORWARD), (0, 2, REVERSE)]
def test_prev_explored(self):
# https://github.com/networkx/networkx/issues/2323
G = nx.DiGraph()
G.add_edges_from([(1, 0), (2, 0), (1, 2), (2, 1)])
pytest.raises(nx.NetworkXNoCycle, find_cycle, G, source=0)
x = list(nx.find_cycle(G, 1))
x_ = [(1, 2), (2, 1)]
assert x == x_
x = list(nx.find_cycle(G, 2))
x_ = [(2, 1), (1, 2)]
assert x == x_
x = list(nx.find_cycle(G))
x_ = [(1, 2), (2, 1)]
assert x == x_
def test_no_cycle(self):
# https://github.com/networkx/networkx/issues/2439
G = nx.DiGraph()
G.add_edges_from([(1, 2), (2, 0), (3, 1), (3, 2)])
pytest.raises(nx.NetworkXNoCycle, find_cycle, G, source=0)
pytest.raises(nx.NetworkXNoCycle, find_cycle, G)
def assert_basis_equal(a, b):
assert sorted(a) == sorted(b)
class TestMinimumCycles:
@classmethod
def setup_class(cls):
T = nx.Graph()
nx.add_cycle(T, [1, 2, 3, 4], weight=1)
T.add_edge(2, 4, weight=5)
cls.diamond_graph = T
def test_unweighted_diamond(self):
mcb = minimum_cycle_basis(self.diamond_graph)
assert_basis_equal([sorted(c) for c in mcb], [[1, 2, 4], [2, 3, 4]])
def test_weighted_diamond(self):
mcb = minimum_cycle_basis(self.diamond_graph, weight="weight")
assert_basis_equal([sorted(c) for c in mcb], [[1, 2, 4], [1, 2, 3, 4]])
def test_dimensionality(self):
# checks |MCB|=|E|-|V|+|NC|
ntrial = 10
for _ in range(ntrial):
rg = nx.erdos_renyi_graph(10, 0.3)
nnodes = rg.number_of_nodes()
nedges = rg.number_of_edges()
ncomp = nx.number_connected_components(rg)
dim_mcb = len(minimum_cycle_basis(rg))
assert dim_mcb == nedges - nnodes + ncomp
def test_complete_graph(self):
cg = nx.complete_graph(5)
mcb = minimum_cycle_basis(cg)
assert all([len(cycle) == 3 for cycle in mcb])
def test_tree_graph(self):
tg = nx.balanced_tree(3, 3)
assert not minimum_cycle_basis(tg)

View file

@ -0,0 +1,156 @@
from itertools import combinations
import pytest
import networkx as nx
def path_graph():
"""Return a path graph of length three."""
G = nx.path_graph(3, create_using=nx.DiGraph)
G.graph["name"] = "path"
nx.freeze(G)
return G
def fork_graph():
"""Return a three node fork graph."""
G = nx.DiGraph(name="fork")
G.add_edges_from([(0, 1), (0, 2)])
nx.freeze(G)
return G
def collider_graph():
"""Return a collider/v-structure graph with three nodes."""
G = nx.DiGraph(name="collider")
G.add_edges_from([(0, 2), (1, 2)])
nx.freeze(G)
return G
def naive_bayes_graph():
"""Return a simply Naive Bayes PGM graph."""
G = nx.DiGraph(name="naive_bayes")
G.add_edges_from([(0, 1), (0, 2), (0, 3), (0, 4)])
nx.freeze(G)
return G
def asia_graph():
"""Return the 'Asia' PGM graph."""
G = nx.DiGraph(name="asia")
G.add_edges_from(
[
("asia", "tuberculosis"),
("smoking", "cancer"),
("smoking", "bronchitis"),
("tuberculosis", "either"),
("cancer", "either"),
("either", "xray"),
("either", "dyspnea"),
("bronchitis", "dyspnea"),
]
)
nx.freeze(G)
return G
@pytest.fixture(name="path_graph")
def path_graph_fixture():
return path_graph()
@pytest.fixture(name="fork_graph")
def fork_graph_fixture():
return fork_graph()
@pytest.fixture(name="collider_graph")
def collider_graph_fixture():
return collider_graph()
@pytest.fixture(name="naive_bayes_graph")
def naive_bayes_graph_fixture():
return naive_bayes_graph()
@pytest.fixture(name="asia_graph")
def asia_graph_fixture():
return asia_graph()
@pytest.mark.parametrize(
"graph",
[path_graph(), fork_graph(), collider_graph(), naive_bayes_graph(), asia_graph()],
)
def test_markov_condition(graph):
"""Test that the Markov condition holds for each PGM graph."""
for node in graph.nodes:
parents = set(graph.predecessors(node))
non_descendants = graph.nodes - nx.descendants(graph, node) - {node} - parents
assert nx.d_separated(graph, {node}, non_descendants, parents)
def test_path_graph_dsep(path_graph):
"""Example-based test of d-separation for path_graph."""
assert nx.d_separated(path_graph, {0}, {2}, {1})
assert not nx.d_separated(path_graph, {0}, {2}, {})
def test_fork_graph_dsep(fork_graph):
"""Example-based test of d-separation for fork_graph."""
assert nx.d_separated(fork_graph, {1}, {2}, {0})
assert not nx.d_separated(fork_graph, {1}, {2}, {})
def test_collider_graph_dsep(collider_graph):
"""Example-based test of d-separation for collider_graph."""
assert nx.d_separated(collider_graph, {0}, {1}, {})
assert not nx.d_separated(collider_graph, {0}, {1}, {2})
def test_naive_bayes_dsep(naive_bayes_graph):
"""Example-based test of d-separation for naive_bayes_graph."""
for u, v in combinations(range(1, 5), 2):
assert nx.d_separated(naive_bayes_graph, {u}, {v}, {0})
assert not nx.d_separated(naive_bayes_graph, {u}, {v}, {})
def test_asia_graph_dsep(asia_graph):
"""Example-based test of d-separation for asia_graph."""
assert nx.d_separated(
asia_graph, {"asia", "smoking"}, {"dyspnea", "xray"}, {"bronchitis", "either"}
)
assert nx.d_separated(
asia_graph, {"tuberculosis", "cancer"}, {"bronchitis"}, {"smoking", "xray"}
)
def test_undirected_graphs_are_not_supported():
"""
Test that undirected graphs are not supported.
d-separation does not apply in the case of undirected graphs.
"""
with pytest.raises(nx.NetworkXNotImplemented):
g = nx.path_graph(3, nx.Graph)
nx.d_separated(g, {0}, {1}, {2})
def test_cyclic_graphs_raise_error():
"""
Test that cycle graphs should cause erroring.
This is because PGMs assume a directed acyclic graph.
"""
with pytest.raises(nx.NetworkXError):
g = nx.cycle_graph(3, nx.DiGraph)
nx.d_separated(g, {0}, {1}, {2})
def test_invalid_nodes_raise_error(asia_graph):
"""
Test that graphs that have invalid nodes passed in raise errors.
"""
with pytest.raises(nx.NodeNotFound):
nx.d_separated(asia_graph, {0}, {1}, {2})

View file

@ -0,0 +1,633 @@
from itertools import combinations, permutations
import pytest
import networkx as nx
from networkx.testing.utils import assert_edges_equal
from networkx.utils import consume
from networkx.utils import pairwise
class TestDagLongestPath:
"""Unit tests computing the longest path in a directed acyclic graph."""
def test_empty(self):
G = nx.DiGraph()
assert nx.dag_longest_path(G) == []
def test_unweighted1(self):
edges = [(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)]
G = nx.DiGraph(edges)
assert nx.dag_longest_path(G) == [1, 2, 3, 5, 6]
def test_unweighted2(self):
edges = [(1, 2), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
G = nx.DiGraph(edges)
assert nx.dag_longest_path(G) == [1, 2, 3, 4, 5]
def test_weighted(self):
G = nx.DiGraph()
edges = [(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]
G.add_weighted_edges_from(edges)
assert nx.dag_longest_path(G) == [2, 3, 5]
def test_undirected_not_implemented(self):
G = nx.Graph()
pytest.raises(nx.NetworkXNotImplemented, nx.dag_longest_path, G)
def test_unorderable_nodes(self):
"""Tests that computing the longest path does not depend on
nodes being orderable.
For more information, see issue #1989.
"""
# Create the directed path graph on four nodes in a diamond shape,
# with nodes represented as (unorderable) Python objects.
nodes = [object() for n in range(4)]
G = nx.DiGraph()
G.add_edge(nodes[0], nodes[1])
G.add_edge(nodes[0], nodes[2])
G.add_edge(nodes[2], nodes[3])
G.add_edge(nodes[1], nodes[3])
# this will raise NotImplementedError when nodes need to be ordered
nx.dag_longest_path(G)
class TestDagLongestPathLength:
"""Unit tests for computing the length of a longest path in a
directed acyclic graph.
"""
def test_unweighted(self):
edges = [(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (5, 7)]
G = nx.DiGraph(edges)
assert nx.dag_longest_path_length(G) == 4
edges = [(1, 2), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
G = nx.DiGraph(edges)
assert nx.dag_longest_path_length(G) == 4
# test degenerate graphs
G = nx.DiGraph()
G.add_node(1)
assert nx.dag_longest_path_length(G) == 0
def test_undirected_not_implemented(self):
G = nx.Graph()
pytest.raises(nx.NetworkXNotImplemented, nx.dag_longest_path_length, G)
def test_weighted(self):
edges = [(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]
G = nx.DiGraph()
G.add_weighted_edges_from(edges)
assert nx.dag_longest_path_length(G) == 5
class TestDAG:
@classmethod
def setup_class(cls):
pass
def test_topological_sort1(self):
DG = nx.DiGraph([(1, 2), (1, 3), (2, 3)])
for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
assert tuple(algorithm(DG)) == (1, 2, 3)
DG.add_edge(3, 2)
for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
pytest.raises(nx.NetworkXUnfeasible, consume, algorithm(DG))
DG.remove_edge(2, 3)
for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
assert tuple(algorithm(DG)) == (1, 3, 2)
DG.remove_edge(3, 2)
assert tuple(nx.topological_sort(DG)) in {(1, 2, 3), (1, 3, 2)}
assert tuple(nx.lexicographical_topological_sort(DG)) == (1, 2, 3)
def test_is_directed_acyclic_graph(self):
G = nx.generators.complete_graph(2)
assert not nx.is_directed_acyclic_graph(G)
assert not nx.is_directed_acyclic_graph(G.to_directed())
assert not nx.is_directed_acyclic_graph(nx.Graph([(3, 4), (4, 5)]))
assert nx.is_directed_acyclic_graph(nx.DiGraph([(3, 4), (4, 5)]))
def test_topological_sort2(self):
DG = nx.DiGraph(
{
1: [2],
2: [3],
3: [4],
4: [5],
5: [1],
11: [12],
12: [13],
13: [14],
14: [15],
}
)
pytest.raises(nx.NetworkXUnfeasible, consume, nx.topological_sort(DG))
assert not nx.is_directed_acyclic_graph(DG)
DG.remove_edge(1, 2)
consume(nx.topological_sort(DG))
assert nx.is_directed_acyclic_graph(DG)
def test_topological_sort3(self):
DG = nx.DiGraph()
DG.add_edges_from([(1, i) for i in range(2, 5)])
DG.add_edges_from([(2, i) for i in range(5, 9)])
DG.add_edges_from([(6, i) for i in range(9, 12)])
DG.add_edges_from([(4, i) for i in range(12, 15)])
def validate(order):
assert isinstance(order, list)
assert set(order) == set(DG)
for u, v in combinations(order, 2):
assert not nx.has_path(DG, v, u)
validate(list(nx.topological_sort(DG)))
DG.add_edge(14, 1)
pytest.raises(nx.NetworkXUnfeasible, consume, nx.topological_sort(DG))
def test_topological_sort4(self):
G = nx.Graph()
G.add_edge(1, 2)
# Only directed graphs can be topologically sorted.
pytest.raises(nx.NetworkXError, consume, nx.topological_sort(G))
def test_topological_sort5(self):
G = nx.DiGraph()
G.add_edge(0, 1)
assert list(nx.topological_sort(G)) == [0, 1]
def test_topological_sort6(self):
for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
def runtime_error():
DG = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
first = True
for x in algorithm(DG):
if first:
first = False
DG.add_edge(5 - x, 5)
def unfeasible_error():
DG = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
first = True
for x in algorithm(DG):
if first:
first = False
DG.remove_node(4)
def runtime_error2():
DG = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
first = True
for x in algorithm(DG):
if first:
first = False
DG.remove_node(2)
pytest.raises(RuntimeError, runtime_error)
pytest.raises(RuntimeError, runtime_error2)
pytest.raises(nx.NetworkXUnfeasible, unfeasible_error)
def test_all_topological_sorts_1(self):
DG = nx.DiGraph([(1, 2), (2, 3), (3, 4), (4, 5)])
assert list(nx.all_topological_sorts(DG)) == [[1, 2, 3, 4, 5]]
def test_all_topological_sorts_2(self):
DG = nx.DiGraph([(1, 3), (2, 1), (2, 4), (4, 3), (4, 5)])
assert sorted(nx.all_topological_sorts(DG)) == [
[2, 1, 4, 3, 5],
[2, 1, 4, 5, 3],
[2, 4, 1, 3, 5],
[2, 4, 1, 5, 3],
[2, 4, 5, 1, 3],
]
def test_all_topological_sorts_3(self):
def unfeasible():
DG = nx.DiGraph([(1, 2), (2, 3), (3, 4), (4, 2), (4, 5)])
# convert to list to execute generator
list(nx.all_topological_sorts(DG))
def not_implemented():
G = nx.Graph([(1, 2), (2, 3)])
# convert to list to execute generator
list(nx.all_topological_sorts(G))
def not_implemted_2():
G = nx.MultiGraph([(1, 2), (1, 2), (2, 3)])
list(nx.all_topological_sorts(G))
pytest.raises(nx.NetworkXUnfeasible, unfeasible)
pytest.raises(nx.NetworkXNotImplemented, not_implemented)
pytest.raises(nx.NetworkXNotImplemented, not_implemted_2)
def test_all_topological_sorts_4(self):
DG = nx.DiGraph()
for i in range(7):
DG.add_node(i)
assert sorted(map(list, permutations(DG.nodes))) == sorted(
nx.all_topological_sorts(DG)
)
def test_all_topological_sorts_multigraph_1(self):
DG = nx.MultiDiGraph([(1, 2), (1, 2), (2, 3), (3, 4), (3, 5), (3, 5), (3, 5)])
assert sorted(nx.all_topological_sorts(DG)) == sorted(
[[1, 2, 3, 4, 5], [1, 2, 3, 5, 4]]
)
def test_all_topological_sorts_multigraph_2(self):
N = 9
edges = []
for i in range(1, N):
edges.extend([(i, i + 1)] * i)
DG = nx.MultiDiGraph(edges)
assert list(nx.all_topological_sorts(DG)) == [list(range(1, N + 1))]
def test_ancestors(self):
G = nx.DiGraph()
ancestors = nx.algorithms.dag.ancestors
G.add_edges_from([(1, 2), (1, 3), (4, 2), (4, 3), (4, 5), (2, 6), (5, 6)])
assert ancestors(G, 6) == {1, 2, 4, 5}
assert ancestors(G, 3) == {1, 4}
assert ancestors(G, 1) == set()
pytest.raises(nx.NetworkXError, ancestors, G, 8)
def test_descendants(self):
G = nx.DiGraph()
descendants = nx.algorithms.dag.descendants
G.add_edges_from([(1, 2), (1, 3), (4, 2), (4, 3), (4, 5), (2, 6), (5, 6)])
assert descendants(G, 1) == {2, 3, 6}
assert descendants(G, 4) == {2, 3, 5, 6}
assert descendants(G, 3) == set()
pytest.raises(nx.NetworkXError, descendants, G, 8)
def test_transitive_closure(self):
G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
assert_edges_equal(nx.transitive_closure(G).edges(), solution)
G = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]
assert_edges_equal(nx.transitive_closure(G).edges(), solution)
G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
solution = [(1, 2), (2, 1), (2, 3), (3, 2), (1, 3), (3, 1)]
soln = sorted(solution + [(n, n) for n in G])
assert_edges_equal(sorted(nx.transitive_closure(G).edges()), soln)
G = nx.Graph([(1, 2), (2, 3), (3, 4)])
pytest.raises(nx.NetworkXNotImplemented, nx.transitive_closure, G)
# test if edge data is copied
G = nx.DiGraph([(1, 2, {"a": 3}), (2, 3, {"b": 0}), (3, 4)])
H = nx.transitive_closure(G)
for u, v in G.edges():
assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
k = 10
G = nx.DiGraph((i, i + 1, {"f": "b", "weight": i}) for i in range(k))
H = nx.transitive_closure(G)
for u, v in G.edges():
assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
def test_reflexive_transitive_closure(self):
G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
soln = sorted(solution + [(n, n) for n in G])
assert_edges_equal(nx.transitive_closure(G).edges(), solution)
assert_edges_equal(nx.transitive_closure(G, False).edges(), solution)
assert_edges_equal(nx.transitive_closure(G, True).edges(), soln)
assert_edges_equal(nx.transitive_closure(G, None).edges(), solution)
G = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]
soln = sorted(solution + [(n, n) for n in G])
assert_edges_equal(nx.transitive_closure(G).edges(), solution)
assert_edges_equal(nx.transitive_closure(G, False).edges(), solution)
assert_edges_equal(nx.transitive_closure(G, True).edges(), soln)
assert_edges_equal(nx.transitive_closure(G, None).edges(), solution)
G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
solution = sorted([(1, 2), (2, 1), (2, 3), (3, 2), (1, 3), (3, 1)])
soln = sorted(solution + [(n, n) for n in G])
assert_edges_equal(sorted(nx.transitive_closure(G).edges()), soln)
assert_edges_equal(sorted(nx.transitive_closure(G, False).edges()), soln)
assert_edges_equal(sorted(nx.transitive_closure(G, None).edges()), solution)
assert_edges_equal(sorted(nx.transitive_closure(G, True).edges()), soln)
def test_transitive_closure_dag(self):
G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
transitive_closure = nx.algorithms.dag.transitive_closure_dag
solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
assert_edges_equal(transitive_closure(G).edges(), solution)
G = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]
assert_edges_equal(transitive_closure(G).edges(), solution)
G = nx.Graph([(1, 2), (2, 3), (3, 4)])
pytest.raises(nx.NetworkXNotImplemented, transitive_closure, G)
# test if edge data is copied
G = nx.DiGraph([(1, 2, {"a": 3}), (2, 3, {"b": 0}), (3, 4)])
H = transitive_closure(G)
for u, v in G.edges():
assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
k = 10
G = nx.DiGraph((i, i + 1, {"foo": "bar", "weight": i}) for i in range(k))
H = transitive_closure(G)
for u, v in G.edges():
assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
def test_transitive_reduction(self):
G = nx.DiGraph([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])
transitive_reduction = nx.algorithms.dag.transitive_reduction
solution = [(1, 2), (2, 3), (3, 4)]
assert_edges_equal(transitive_reduction(G).edges(), solution)
G = nx.DiGraph([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)])
transitive_reduction = nx.algorithms.dag.transitive_reduction
solution = [(1, 2), (2, 3), (2, 4)]
assert_edges_equal(transitive_reduction(G).edges(), solution)
G = nx.Graph([(1, 2), (2, 3), (3, 4)])
pytest.raises(nx.NetworkXNotImplemented, transitive_reduction, G)
def _check_antichains(self, solution, result):
sol = [frozenset(a) for a in solution]
res = [frozenset(a) for a in result]
assert set(sol) == set(res)
def test_antichains(self):
antichains = nx.algorithms.dag.antichains
G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
solution = [[], [4], [3], [2], [1]]
self._check_antichains(list(antichains(G)), solution)
G = nx.DiGraph([(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (5, 7)])
solution = [
[],
[4],
[7],
[7, 4],
[6],
[6, 4],
[6, 7],
[6, 7, 4],
[5],
[5, 4],
[3],
[3, 4],
[2],
[1],
]
self._check_antichains(list(antichains(G)), solution)
G = nx.DiGraph([(1, 2), (1, 3), (3, 4), (3, 5), (5, 6)])
solution = [
[],
[6],
[5],
[4],
[4, 6],
[4, 5],
[3],
[2],
[2, 6],
[2, 5],
[2, 4],
[2, 4, 6],
[2, 4, 5],
[2, 3],
[1],
]
self._check_antichains(list(antichains(G)), solution)
G = nx.DiGraph({0: [1, 2], 1: [4], 2: [3], 3: [4]})
solution = [[], [4], [3], [2], [1], [1, 3], [1, 2], [0]]
self._check_antichains(list(antichains(G)), solution)
G = nx.DiGraph()
self._check_antichains(list(antichains(G)), [[]])
G = nx.DiGraph()
G.add_nodes_from([0, 1, 2])
solution = [[], [0], [1], [1, 0], [2], [2, 0], [2, 1], [2, 1, 0]]
self._check_antichains(list(antichains(G)), solution)
def f(x):
return list(antichains(x))
G = nx.Graph([(1, 2), (2, 3), (3, 4)])
pytest.raises(nx.NetworkXNotImplemented, f, G)
G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
pytest.raises(nx.NetworkXUnfeasible, f, G)
def test_lexicographical_topological_sort(self):
G = nx.DiGraph([(1, 2), (2, 3), (1, 4), (1, 5), (2, 6)])
assert list(nx.lexicographical_topological_sort(G)) == [1, 2, 3, 4, 5, 6]
assert list(nx.lexicographical_topological_sort(G, key=lambda x: x)) == [
1,
2,
3,
4,
5,
6,
]
assert list(nx.lexicographical_topological_sort(G, key=lambda x: -x)) == [
1,
5,
4,
2,
6,
3,
]
def test_lexicographical_topological_sort2(self):
"""
Check the case of two or more nodes with same key value.
Want to avoid exception raised due to comparing nodes directly.
See Issue #3493
"""
class Test_Node:
def __init__(self, n):
self.label = n
self.priority = 1
def __repr__(self):
return f"Node({self.label})"
def sorting_key(node):
return node.priority
test_nodes = [Test_Node(n) for n in range(4)]
G = nx.DiGraph()
edges = [(0, 1), (0, 2), (0, 3), (2, 3)]
G.add_edges_from((test_nodes[a], test_nodes[b]) for a, b in edges)
sorting = list(nx.lexicographical_topological_sort(G, key=sorting_key))
assert sorting == test_nodes
def test_is_aperiodic_cycle():
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3, 4])
assert not nx.is_aperiodic(G)
def test_is_aperiodic_cycle2():
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3, 4])
nx.add_cycle(G, [3, 4, 5, 6, 7])
assert nx.is_aperiodic(G)
def test_is_aperiodic_cycle3():
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3, 4])
nx.add_cycle(G, [3, 4, 5, 6])
assert not nx.is_aperiodic(G)
def test_is_aperiodic_cycle4():
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3, 4])
G.add_edge(1, 3)
assert nx.is_aperiodic(G)
def test_is_aperiodic_selfloop():
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3, 4])
G.add_edge(1, 1)
assert nx.is_aperiodic(G)
def test_is_aperiodic_raise():
G = nx.Graph()
pytest.raises(nx.NetworkXError, nx.is_aperiodic, G)
def test_is_aperiodic_bipartite():
# Bipartite graph
G = nx.DiGraph(nx.davis_southern_women_graph())
assert not nx.is_aperiodic(G)
def test_is_aperiodic_rary_tree():
G = nx.full_rary_tree(3, 27, create_using=nx.DiGraph())
assert not nx.is_aperiodic(G)
def test_is_aperiodic_disconnected():
# disconnected graph
G = nx.DiGraph()
nx.add_cycle(G, [1, 2, 3, 4])
nx.add_cycle(G, [5, 6, 7, 8])
assert not nx.is_aperiodic(G)
G.add_edge(1, 3)
G.add_edge(5, 7)
assert nx.is_aperiodic(G)
def test_is_aperiodic_disconnected2():
G = nx.DiGraph()
nx.add_cycle(G, [0, 1, 2])
G.add_edge(3, 3)
assert not nx.is_aperiodic(G)
class TestDagToBranching:
"""Unit tests for the :func:`networkx.dag_to_branching` function."""
def test_single_root(self):
"""Tests that a directed acyclic graph with a single degree
zero node produces an arborescence.
"""
G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3)])
B = nx.dag_to_branching(G)
expected = nx.DiGraph([(0, 1), (1, 3), (0, 2), (2, 4)])
assert nx.is_arborescence(B)
assert nx.is_isomorphic(B, expected)
def test_multiple_roots(self):
"""Tests that a directed acyclic graph with multiple degree zero
nodes creates an arborescence with multiple (weakly) connected
components.
"""
G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3), (5, 2)])
B = nx.dag_to_branching(G)
expected = nx.DiGraph([(0, 1), (1, 3), (0, 2), (2, 4), (5, 6), (6, 7)])
assert nx.is_branching(B)
assert not nx.is_arborescence(B)
assert nx.is_isomorphic(B, expected)
# # Attributes are not copied by this function. If they were, this would
# # be a good test to uncomment.
# def test_copy_attributes(self):
# """Tests that node attributes are copied in the branching."""
# G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3)])
# for v in G:
# G.node[v]['label'] = str(v)
# B = nx.dag_to_branching(G)
# # Determine the root node of the branching.
# root = next(v for v, d in B.in_degree() if d == 0)
# assert_equal(B.node[root]['label'], '0')
# children = B[root]
# # Get the left and right children, nodes 1 and 2, respectively.
# left, right = sorted(children, key=lambda v: B.node[v]['label'])
# assert_equal(B.node[left]['label'], '1')
# assert_equal(B.node[right]['label'], '2')
# # Get the left grandchild.
# children = B[left]
# assert_equal(len(children), 1)
# left_grandchild = arbitrary_element(children)
# assert_equal(B.node[left_grandchild]['label'], '3')
# # Get the right grandchild.
# children = B[right]
# assert_equal(len(children), 1)
# right_grandchild = arbitrary_element(children)
# assert_equal(B.node[right_grandchild]['label'], '3')
def test_already_arborescence(self):
"""Tests that a directed acyclic graph that is already an
arborescence produces an isomorphic arborescence as output.
"""
A = nx.balanced_tree(2, 2, create_using=nx.DiGraph())
B = nx.dag_to_branching(A)
assert nx.is_isomorphic(A, B)
def test_already_branching(self):
"""Tests that a directed acyclic graph that is already a
branching produces an isomorphic branching as output.
"""
T1 = nx.balanced_tree(2, 2, create_using=nx.DiGraph())
T2 = nx.balanced_tree(2, 2, create_using=nx.DiGraph())
G = nx.disjoint_union(T1, T2)
B = nx.dag_to_branching(G)
assert nx.is_isomorphic(G, B)
def test_not_acyclic(self):
"""Tests that a non-acyclic graph causes an exception."""
with pytest.raises(nx.HasACycle):
G = nx.DiGraph(pairwise("abc", cyclic=True))
nx.dag_to_branching(G)
def test_undirected(self):
with pytest.raises(nx.NetworkXNotImplemented):
nx.dag_to_branching(nx.Graph())
def test_multigraph(self):
with pytest.raises(nx.NetworkXNotImplemented):
nx.dag_to_branching(nx.MultiGraph())
def test_multidigraph(self):
with pytest.raises(nx.NetworkXNotImplemented):
nx.dag_to_branching(nx.MultiDiGraph())

View file

@ -0,0 +1,272 @@
from random import Random
import pytest
import networkx as nx
from networkx import convert_node_labels_to_integers as cnlti
class TestDistance:
def setup_method(self):
G = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
self.G = G
def test_eccentricity(self):
assert nx.eccentricity(self.G, 1) == 6
e = nx.eccentricity(self.G)
assert e[1] == 6
sp = dict(nx.shortest_path_length(self.G))
e = nx.eccentricity(self.G, sp=sp)
assert e[1] == 6
e = nx.eccentricity(self.G, v=1)
assert e == 6
# This behavior changed in version 1.8 (ticket #739)
e = nx.eccentricity(self.G, v=[1, 1])
assert e[1] == 6
e = nx.eccentricity(self.G, v=[1, 2])
assert e[1] == 6
# test against graph with one node
G = nx.path_graph(1)
e = nx.eccentricity(G)
assert e[0] == 0
e = nx.eccentricity(G, v=0)
assert e == 0
pytest.raises(nx.NetworkXError, nx.eccentricity, G, 1)
# test against empty graph
G = nx.empty_graph()
e = nx.eccentricity(G)
assert e == {}
def test_diameter(self):
assert nx.diameter(self.G) == 6
def test_radius(self):
assert nx.radius(self.G) == 4
def test_periphery(self):
assert set(nx.periphery(self.G)) == {1, 4, 13, 16}
def test_center(self):
assert set(nx.center(self.G)) == {6, 7, 10, 11}
def test_bound_diameter(self):
assert nx.diameter(self.G, usebounds=True) == 6
def test_bound_radius(self):
assert nx.radius(self.G, usebounds=True) == 4
def test_bound_periphery(self):
result = {1, 4, 13, 16}
assert set(nx.periphery(self.G, usebounds=True)) == result
def test_bound_center(self):
result = {6, 7, 10, 11}
assert set(nx.center(self.G, usebounds=True)) == result
def test_radius_exception(self):
G = nx.Graph()
G.add_edge(1, 2)
G.add_edge(3, 4)
pytest.raises(nx.NetworkXError, nx.diameter, G)
def test_eccentricity_infinite(self):
with pytest.raises(nx.NetworkXError):
G = nx.Graph([(1, 2), (3, 4)])
e = nx.eccentricity(G)
def test_eccentricity_undirected_not_connected(self):
with pytest.raises(nx.NetworkXError):
G = nx.Graph([(1, 2), (3, 4)])
e = nx.eccentricity(G, sp=1)
def test_eccentricity_directed_weakly_connected(self):
with pytest.raises(nx.NetworkXError):
DG = nx.DiGraph([(1, 2), (1, 3)])
nx.eccentricity(DG)
class TestResistanceDistance:
@classmethod
def setup_class(cls):
global np
global sp_sparse
np = pytest.importorskip("numpy")
scipy = pytest.importorskip("scipy")
sp_sparse = pytest.importorskip("scipy.sparse")
def setup_method(self):
G = nx.Graph()
G.add_edge(1, 2, weight=2)
G.add_edge(2, 3, weight=4)
G.add_edge(3, 4, weight=1)
G.add_edge(1, 4, weight=3)
self.G = G
def test_laplacian_submatrix(self):
from networkx.algorithms.distance_measures import _laplacian_submatrix
M = sp_sparse.csr_matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32)
N = sp_sparse.csr_matrix([[5, 6], [8, 9]], dtype=np.float32)
Mn, Mn_nodelist = _laplacian_submatrix(1, M, [1, 2, 3])
assert Mn_nodelist == [2, 3]
assert np.allclose(Mn.toarray(), N.toarray())
def test_laplacian_submatrix_square(self):
with pytest.raises(nx.NetworkXError):
from networkx.algorithms.distance_measures import _laplacian_submatrix
M = sp_sparse.csr_matrix([[1, 2], [4, 5], [7, 8]], dtype=np.float32)
_laplacian_submatrix(1, M, [1, 2, 3])
def test_laplacian_submatrix_matrix_node_dim(self):
with pytest.raises(nx.NetworkXError):
from networkx.algorithms.distance_measures import _laplacian_submatrix
M = sp_sparse.csr_matrix(
[[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32
)
_laplacian_submatrix(1, M, [1, 2, 3, 4])
def test_resistance_distance(self):
rd = nx.resistance_distance(self.G, 1, 3, "weight", True)
test_data = 1 / (1 / (2 + 4) + 1 / (1 + 3))
assert round(rd, 5) == round(test_data, 5)
def test_resistance_distance_noinv(self):
rd = nx.resistance_distance(self.G, 1, 3, "weight", False)
test_data = 1 / (1 / (1 / 2 + 1 / 4) + 1 / (1 / 1 + 1 / 3))
assert round(rd, 5) == round(test_data, 5)
def test_resistance_distance_no_weight(self):
rd = nx.resistance_distance(self.G, 1, 3)
assert round(rd, 5) == 1
def test_resistance_distance_neg_weight(self):
self.G[2][3]["weight"] = -4
rd = nx.resistance_distance(self.G, 1, 3, "weight", True)
test_data = 1 / (1 / (2 + -4) + 1 / (1 + 3))
assert round(rd, 5) == round(test_data, 5)
def test_multigraph(self):
G = nx.MultiGraph()
G.add_edge(1, 2, weight=2)
G.add_edge(2, 3, weight=4)
G.add_edge(3, 4, weight=1)
G.add_edge(1, 4, weight=3)
rd = nx.resistance_distance(G, 1, 3, "weight", True)
assert np.isclose(rd, 1 / (1 / (2 + 4) + 1 / (1 + 3)))
def test_resistance_distance_div0(self):
with pytest.raises(ZeroDivisionError):
self.G[1][2]["weight"] = 0
nx.resistance_distance(self.G, 1, 3, "weight")
def test_resistance_distance_not_connected(self):
with pytest.raises(nx.NetworkXError):
self.G.add_node(5)
nx.resistance_distance(self.G, 1, 5)
def test_resistance_distance_same_node(self):
with pytest.raises(nx.NetworkXError):
nx.resistance_distance(self.G, 1, 1)
def test_resistance_distance_nodeA_not_in_graph(self):
with pytest.raises(nx.NetworkXError):
nx.resistance_distance(self.G, 9, 1)
def test_resistance_distance_nodeB_not_in_graph(self):
with pytest.raises(nx.NetworkXError):
nx.resistance_distance(self.G, 1, 9)
class TestBarycenter:
"""Test :func:`networkx.algorithms.distance_measures.barycenter`."""
def barycenter_as_subgraph(self, g, **kwargs):
"""Return the subgraph induced on the barycenter of g"""
b = nx.barycenter(g, **kwargs)
assert isinstance(b, list)
assert set(b) <= set(g)
return g.subgraph(b)
def test_must_be_connected(self):
pytest.raises(nx.NetworkXNoPath, nx.barycenter, nx.empty_graph(5))
def test_sp_kwarg(self):
# Complete graph K_5. Normally it works...
K_5 = nx.complete_graph(5)
sp = dict(nx.shortest_path_length(K_5))
assert nx.barycenter(K_5, sp=sp) == list(K_5)
# ...but not with the weight argument
for u, v, data in K_5.edges.data():
data["weight"] = 1
pytest.raises(ValueError, nx.barycenter, K_5, sp=sp, weight="weight")
# ...and a corrupted sp can make it seem like K_5 is disconnected
del sp[0][1]
pytest.raises(nx.NetworkXNoPath, nx.barycenter, K_5, sp=sp)
def test_trees(self):
"""The barycenter of a tree is a single vertex or an edge.
See [West01]_, p. 78.
"""
prng = Random(0xDEADBEEF)
for i in range(50):
RT = nx.random_tree(prng.randint(1, 75), prng)
b = self.barycenter_as_subgraph(RT)
if len(b) == 2:
assert b.size() == 1
else:
assert len(b) == 1
assert b.size() == 0
def test_this_one_specific_tree(self):
"""Test the tree pictured at the bottom of [West01]_, p. 78."""
g = nx.Graph(
{
"a": ["b"],
"b": ["a", "x"],
"x": ["b", "y"],
"y": ["x", "z"],
"z": ["y", 0, 1, 2, 3, 4],
0: ["z"],
1: ["z"],
2: ["z"],
3: ["z"],
4: ["z"],
}
)
b = self.barycenter_as_subgraph(g, attr="barycentricity")
assert list(b) == ["z"]
assert not b.edges
expected_barycentricity = {
0: 23,
1: 23,
2: 23,
3: 23,
4: 23,
"a": 35,
"b": 27,
"x": 21,
"y": 17,
"z": 15,
}
for node, barycentricity in expected_barycentricity.items():
assert g.nodes[node]["barycentricity"] == barycentricity
# Doubling weights should do nothing but double the barycentricities
for edge in g.edges:
g.edges[edge]["weight"] = 2
b = self.barycenter_as_subgraph(g, weight="weight", attr="barycentricity2")
assert list(b) == ["z"]
assert not b.edges
for node, barycentricity in expected_barycentricity.items():
assert g.nodes[node]["barycentricity2"] == barycentricity * 2

View file

@ -0,0 +1,66 @@
import networkx as nx
from networkx import is_strongly_regular
class TestDistanceRegular:
def test_is_distance_regular(self):
assert nx.is_distance_regular(nx.icosahedral_graph())
assert nx.is_distance_regular(nx.petersen_graph())
assert nx.is_distance_regular(nx.cubical_graph())
assert nx.is_distance_regular(nx.complete_bipartite_graph(3, 3))
assert nx.is_distance_regular(nx.tetrahedral_graph())
assert nx.is_distance_regular(nx.dodecahedral_graph())
assert nx.is_distance_regular(nx.pappus_graph())
assert nx.is_distance_regular(nx.heawood_graph())
assert nx.is_distance_regular(nx.cycle_graph(3))
# no distance regular
assert not nx.is_distance_regular(nx.path_graph(4))
def test_not_connected(self):
G = nx.cycle_graph(4)
nx.add_cycle(G, [5, 6, 7])
assert not nx.is_distance_regular(G)
def test_global_parameters(self):
b, c = nx.intersection_array(nx.cycle_graph(5))
g = nx.global_parameters(b, c)
assert list(g) == [(0, 0, 2), (1, 0, 1), (1, 1, 0)]
b, c = nx.intersection_array(nx.cycle_graph(3))
g = nx.global_parameters(b, c)
assert list(g) == [(0, 0, 2), (1, 1, 0)]
def test_intersection_array(self):
b, c = nx.intersection_array(nx.cycle_graph(5))
assert b == [2, 1]
assert c == [1, 1]
b, c = nx.intersection_array(nx.dodecahedral_graph())
assert b == [3, 2, 1, 1, 1]
assert c == [1, 1, 1, 2, 3]
b, c = nx.intersection_array(nx.icosahedral_graph())
assert b == [5, 2, 1]
assert c == [1, 2, 5]
class TestStronglyRegular:
"""Unit tests for the :func:`~networkx.is_strongly_regular`
function.
"""
def test_cycle_graph(self):
"""Tests that the cycle graph on five vertices is strongly
regular.
"""
G = nx.cycle_graph(5)
assert is_strongly_regular(G)
def test_petersen_graph(self):
"""Tests that the Petersen graph is strongly regular."""
G = nx.petersen_graph()
assert is_strongly_regular(G)
def test_path_graph(self):
"""Tests that the path graph is not strongly regular."""
G = nx.path_graph(4)
assert not is_strongly_regular(G)

View file

@ -0,0 +1,284 @@
import networkx as nx
import pytest
class TestImmediateDominators:
def test_exceptions(self):
G = nx.Graph()
G.add_node(0)
pytest.raises(nx.NetworkXNotImplemented, nx.immediate_dominators, G, 0)
G = nx.MultiGraph(G)
pytest.raises(nx.NetworkXNotImplemented, nx.immediate_dominators, G, 0)
G = nx.DiGraph([[0, 0]])
pytest.raises(nx.NetworkXError, nx.immediate_dominators, G, 1)
def test_singleton(self):
G = nx.DiGraph()
G.add_node(0)
assert nx.immediate_dominators(G, 0) == {0: 0}
G.add_edge(0, 0)
assert nx.immediate_dominators(G, 0) == {0: 0}
def test_path(self):
n = 5
G = nx.path_graph(n, create_using=nx.DiGraph())
assert nx.immediate_dominators(G, 0) == {i: max(i - 1, 0) for i in range(n)}
def test_cycle(self):
n = 5
G = nx.cycle_graph(n, create_using=nx.DiGraph())
assert nx.immediate_dominators(G, 0) == {i: max(i - 1, 0) for i in range(n)}
def test_unreachable(self):
n = 5
assert n > 1
G = nx.path_graph(n, create_using=nx.DiGraph())
assert nx.immediate_dominators(G, n // 2) == {
i: max(i - 1, n // 2) for i in range(n // 2, n)
}
def test_irreducible1(self):
# Graph taken from Figure 2 of
# K. D. Cooper, T. J. Harvey, and K. Kennedy.
# A simple, fast dominance algorithm.
# Software Practice & Experience, 4:110, 2001.
edges = [(1, 2), (2, 1), (3, 2), (4, 1), (5, 3), (5, 4)]
G = nx.DiGraph(edges)
assert nx.immediate_dominators(G, 5) == {i: 5 for i in range(1, 6)}
def test_irreducible2(self):
# Graph taken from Figure 4 of
# K. D. Cooper, T. J. Harvey, and K. Kennedy.
# A simple, fast dominance algorithm.
# Software Practice & Experience, 4:110, 2001.
edges = [(1, 2), (2, 1), (2, 3), (3, 2), (4, 2), (4, 3), (5, 1), (6, 4), (6, 5)]
G = nx.DiGraph(edges)
result = nx.immediate_dominators(G, 6)
assert result == {i: 6 for i in range(1, 7)}
def test_domrel_png(self):
# Graph taken from https://commons.wikipedia.org/wiki/File:Domrel.png
edges = [(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)]
G = nx.DiGraph(edges)
result = nx.immediate_dominators(G, 1)
assert result == {1: 1, 2: 1, 3: 2, 4: 2, 5: 2, 6: 2}
# Test postdominance.
result = nx.immediate_dominators(G.reverse(copy=False), 6)
assert result == {1: 2, 2: 6, 3: 5, 4: 5, 5: 2, 6: 6}
def test_boost_example(self):
# Graph taken from Figure 1 of
# http://www.boost.org/doc/libs/1_56_0/libs/graph/doc/lengauer_tarjan_dominator.htm
edges = [(0, 1), (1, 2), (1, 3), (2, 7), (3, 4), (4, 5), (4, 6), (5, 7), (6, 4)]
G = nx.DiGraph(edges)
result = nx.immediate_dominators(G, 0)
assert result == {0: 0, 1: 0, 2: 1, 3: 1, 4: 3, 5: 4, 6: 4, 7: 1}
# Test postdominance.
result = nx.immediate_dominators(G.reverse(copy=False), 7)
assert result == {0: 1, 1: 7, 2: 7, 3: 4, 4: 5, 5: 7, 6: 4, 7: 7}
class TestDominanceFrontiers:
def test_exceptions(self):
G = nx.Graph()
G.add_node(0)
pytest.raises(nx.NetworkXNotImplemented, nx.dominance_frontiers, G, 0)
G = nx.MultiGraph(G)
pytest.raises(nx.NetworkXNotImplemented, nx.dominance_frontiers, G, 0)
G = nx.DiGraph([[0, 0]])
pytest.raises(nx.NetworkXError, nx.dominance_frontiers, G, 1)
def test_singleton(self):
G = nx.DiGraph()
G.add_node(0)
assert nx.dominance_frontiers(G, 0) == {0: set()}
G.add_edge(0, 0)
assert nx.dominance_frontiers(G, 0) == {0: set()}
def test_path(self):
n = 5
G = nx.path_graph(n, create_using=nx.DiGraph())
assert nx.dominance_frontiers(G, 0) == {i: set() for i in range(n)}
def test_cycle(self):
n = 5
G = nx.cycle_graph(n, create_using=nx.DiGraph())
assert nx.dominance_frontiers(G, 0) == {i: set() for i in range(n)}
def test_unreachable(self):
n = 5
assert n > 1
G = nx.path_graph(n, create_using=nx.DiGraph())
assert nx.dominance_frontiers(G, n // 2) == {i: set() for i in range(n // 2, n)}
def test_irreducible1(self):
# Graph taken from Figure 2 of
# K. D. Cooper, T. J. Harvey, and K. Kennedy.
# A simple, fast dominance algorithm.
# Software Practice & Experience, 4:110, 2001.
edges = [(1, 2), (2, 1), (3, 2), (4, 1), (5, 3), (5, 4)]
G = nx.DiGraph(edges)
assert {u: df for u, df in nx.dominance_frontiers(G, 5).items()} == {
1: {2},
2: {1},
3: {2},
4: {1},
5: set(),
}
def test_irreducible2(self):
# Graph taken from Figure 4 of
# K. D. Cooper, T. J. Harvey, and K. Kennedy.
# A simple, fast dominance algorithm.
# Software Practice & Experience, 4:110, 2001.
edges = [(1, 2), (2, 1), (2, 3), (3, 2), (4, 2), (4, 3), (5, 1), (6, 4), (6, 5)]
G = nx.DiGraph(edges)
assert nx.dominance_frontiers(G, 6) == {
1: {2},
2: {1, 3},
3: {2},
4: {2, 3},
5: {1},
6: set(),
}
def test_domrel_png(self):
# Graph taken from https://commons.wikipedia.org/wiki/File:Domrel.png
edges = [(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)]
G = nx.DiGraph(edges)
assert nx.dominance_frontiers(G, 1) == {
1: set(),
2: {2},
3: {5},
4: {5},
5: {2},
6: set(),
}
# Test postdominance.
result = nx.dominance_frontiers(G.reverse(copy=False), 6)
assert result == {1: set(), 2: {2}, 3: {2}, 4: {2}, 5: {2}, 6: set()}
def test_boost_example(self):
# Graph taken from Figure 1 of
# http://www.boost.org/doc/libs/1_56_0/libs/graph/doc/lengauer_tarjan_dominator.htm
edges = [(0, 1), (1, 2), (1, 3), (2, 7), (3, 4), (4, 5), (4, 6), (5, 7), (6, 4)]
G = nx.DiGraph(edges)
assert nx.dominance_frontiers(G, 0) == {
0: set(),
1: set(),
2: {7},
3: {7},
4: {4, 7},
5: {7},
6: {4},
7: set(),
}
# Test postdominance.
result = nx.dominance_frontiers(G.reverse(copy=False), 7)
expected = {
0: set(),
1: set(),
2: {1},
3: {1},
4: {1, 4},
5: {1},
6: {4},
7: set(),
}
assert result == expected
def test_discard_issue(self):
# https://github.com/networkx/networkx/issues/2071
g = nx.DiGraph()
g.add_edges_from(
[
("b0", "b1"),
("b1", "b2"),
("b2", "b3"),
("b3", "b1"),
("b1", "b5"),
("b5", "b6"),
("b5", "b8"),
("b6", "b7"),
("b8", "b7"),
("b7", "b3"),
("b3", "b4"),
]
)
df = nx.dominance_frontiers(g, "b0")
assert df == {
"b4": set(),
"b5": {"b3"},
"b6": {"b7"},
"b7": {"b3"},
"b0": set(),
"b1": {"b1"},
"b2": {"b3"},
"b3": {"b1"},
"b8": {"b7"},
}
def test_loop(self):
g = nx.DiGraph()
g.add_edges_from([("a", "b"), ("b", "c"), ("b", "a")])
df = nx.dominance_frontiers(g, "a")
assert df == {"a": set(), "b": set(), "c": set()}
def test_missing_immediate_doms(self):
# see https://github.com/networkx/networkx/issues/2070
g = nx.DiGraph()
edges = [
("entry_1", "b1"),
("b1", "b2"),
("b2", "b3"),
("b3", "exit"),
("entry_2", "b3"),
]
# entry_1
# |
# b1
# |
# b2 entry_2
# | /
# b3
# |
# exit
g.add_edges_from(edges)
# formerly raised KeyError on entry_2 when parsing b3
# because entry_2 does not have immediate doms (no path)
nx.dominance_frontiers(g, "entry_1")
def test_loops_larger(self):
# from
# http://ecee.colorado.edu/~waite/Darmstadt/motion.html
g = nx.DiGraph()
edges = [
("entry", "exit"),
("entry", "1"),
("1", "2"),
("2", "3"),
("3", "4"),
("4", "5"),
("5", "6"),
("6", "exit"),
("6", "2"),
("5", "3"),
("4", "4"),
]
g.add_edges_from(edges)
df = nx.dominance_frontiers(g, "entry")
answer = {
"entry": set(),
"1": {"exit"},
"2": {"exit", "2"},
"3": {"exit", "3", "2"},
"4": {"exit", "4", "3", "2"},
"5": {"exit", "3", "2"},
"6": {"exit", "2"},
"exit": set(),
}
for n in df:
assert set(df[n]) == set(answer[n])

View file

@ -0,0 +1,46 @@
import pytest
import networkx as nx
def test_dominating_set():
G = nx.gnp_random_graph(100, 0.1)
D = nx.dominating_set(G)
assert nx.is_dominating_set(G, D)
D = nx.dominating_set(G, start_with=0)
assert nx.is_dominating_set(G, D)
def test_complete():
""" In complete graphs each node is a dominating set.
Thus the dominating set has to be of cardinality 1.
"""
K4 = nx.complete_graph(4)
assert len(nx.dominating_set(K4)) == 1
K5 = nx.complete_graph(5)
assert len(nx.dominating_set(K5)) == 1
def test_raise_dominating_set():
with pytest.raises(nx.NetworkXError):
G = nx.path_graph(4)
D = nx.dominating_set(G, start_with=10)
def test_is_dominating_set():
G = nx.path_graph(4)
d = {1, 3}
assert nx.is_dominating_set(G, d)
d = {0, 2}
assert nx.is_dominating_set(G, d)
d = {1}
assert not nx.is_dominating_set(G, d)
def test_wikipedia_is_dominating_set():
"""Example from https://en.wikipedia.org/wiki/Dominating_set
"""
G = nx.cycle_graph(4)
G.add_edges_from([(0, 4), (1, 4), (2, 5)])
assert nx.is_dominating_set(G, {4, 3, 5})
assert nx.is_dominating_set(G, {0, 2})
assert nx.is_dominating_set(G, {1, 2})

View file

@ -0,0 +1,58 @@
"""Unit tests for the :mod:`networkx.algorithms.efficiency` module."""
import networkx as nx
class TestEfficiency:
def setup_method(self):
# G1 is a disconnected graph
self.G1 = nx.Graph()
self.G1.add_nodes_from([1, 2, 3])
# G2 is a cycle graph
self.G2 = nx.cycle_graph(4)
# G3 is the triangle graph with one additional edge
self.G3 = nx.lollipop_graph(3, 1)
def test_efficiency_disconnected_nodes(self):
"""
When nodes are disconnected, efficiency is 0
"""
assert nx.efficiency(self.G1, 1, 2) == 0
def test_local_efficiency_disconnected_graph(self):
"""
In a disconnected graph the efficiency is 0
"""
assert nx.local_efficiency(self.G1) == 0
def test_efficiency(self):
assert nx.efficiency(self.G2, 0, 1) == 1
assert nx.efficiency(self.G2, 0, 2) == 1 / 2
def test_global_efficiency(self):
assert nx.global_efficiency(self.G2) == 5 / 6
def test_global_efficiency_complete_graph(self):
"""
Tests that the average global efficiency of the complete graph is one.
"""
for n in range(2, 10):
G = nx.complete_graph(n)
assert nx.global_efficiency(G) == 1
def test_local_efficiency_complete_graph(self):
"""
Test that the local efficiency for a complete graph with at least 3
nodes should be one. For a graph with only 2 nodes, the induced
subgraph has no edges.
"""
for n in range(3, 10):
G = nx.complete_graph(n)
assert nx.local_efficiency(G) == 1
def test_using_ego_graph(self):
"""
Test that the ego graph is used when computing local efficiency.
For more information, see GitHub issue #2710.
"""
assert nx.local_efficiency(self.G3) == 7 / 12

View file

@ -0,0 +1,191 @@
import collections
import pytest
import networkx as nx
class TestIsEulerian:
def test_is_eulerian(self):
assert nx.is_eulerian(nx.complete_graph(5))
assert nx.is_eulerian(nx.complete_graph(7))
assert nx.is_eulerian(nx.hypercube_graph(4))
assert nx.is_eulerian(nx.hypercube_graph(6))
assert not nx.is_eulerian(nx.complete_graph(4))
assert not nx.is_eulerian(nx.complete_graph(6))
assert not nx.is_eulerian(nx.hypercube_graph(3))
assert not nx.is_eulerian(nx.hypercube_graph(5))
assert not nx.is_eulerian(nx.petersen_graph())
assert not nx.is_eulerian(nx.path_graph(4))
def test_is_eulerian2(self):
# not connected
G = nx.Graph()
G.add_nodes_from([1, 2, 3])
assert not nx.is_eulerian(G)
# not strongly connected
G = nx.DiGraph()
G.add_nodes_from([1, 2, 3])
assert not nx.is_eulerian(G)
G = nx.MultiDiGraph()
G.add_edge(1, 2)
G.add_edge(2, 3)
G.add_edge(2, 3)
G.add_edge(3, 1)
assert not nx.is_eulerian(G)
class TestEulerianCircuit:
def test_eulerian_circuit_cycle(self):
G = nx.cycle_graph(4)
edges = list(nx.eulerian_circuit(G, source=0))
nodes = [u for u, v in edges]
assert nodes == [0, 3, 2, 1]
assert edges == [(0, 3), (3, 2), (2, 1), (1, 0)]
edges = list(nx.eulerian_circuit(G, source=1))
nodes = [u for u, v in edges]
assert nodes == [1, 2, 3, 0]
assert edges == [(1, 2), (2, 3), (3, 0), (0, 1)]
G = nx.complete_graph(3)
edges = list(nx.eulerian_circuit(G, source=0))
nodes = [u for u, v in edges]
assert nodes == [0, 2, 1]
assert edges == [(0, 2), (2, 1), (1, 0)]
edges = list(nx.eulerian_circuit(G, source=1))
nodes = [u for u, v in edges]
assert nodes == [1, 2, 0]
assert edges == [(1, 2), (2, 0), (0, 1)]
def test_eulerian_circuit_digraph(self):
G = nx.DiGraph()
nx.add_cycle(G, [0, 1, 2, 3])
edges = list(nx.eulerian_circuit(G, source=0))
nodes = [u for u, v in edges]
assert nodes == [0, 1, 2, 3]
assert edges == [(0, 1), (1, 2), (2, 3), (3, 0)]
edges = list(nx.eulerian_circuit(G, source=1))
nodes = [u for u, v in edges]
assert nodes == [1, 2, 3, 0]
assert edges == [(1, 2), (2, 3), (3, 0), (0, 1)]
def test_multigraph(self):
G = nx.MultiGraph()
nx.add_cycle(G, [0, 1, 2, 3])
G.add_edge(1, 2)
G.add_edge(1, 2)
edges = list(nx.eulerian_circuit(G, source=0))
nodes = [u for u, v in edges]
assert nodes == [0, 3, 2, 1, 2, 1]
assert edges == [(0, 3), (3, 2), (2, 1), (1, 2), (2, 1), (1, 0)]
def test_multigraph_with_keys(self):
G = nx.MultiGraph()
nx.add_cycle(G, [0, 1, 2, 3])
G.add_edge(1, 2)
G.add_edge(1, 2)
edges = list(nx.eulerian_circuit(G, source=0, keys=True))
nodes = [u for u, v, k in edges]
assert nodes == [0, 3, 2, 1, 2, 1]
assert edges[:2] == [(0, 3, 0), (3, 2, 0)]
assert collections.Counter(edges[2:5]) == collections.Counter(
[(2, 1, 0), (1, 2, 1), (2, 1, 2)]
)
assert edges[5:] == [(1, 0, 0)]
def test_not_eulerian(self):
with pytest.raises(nx.NetworkXError):
f = list(nx.eulerian_circuit(nx.complete_graph(4)))
class TestIsSemiEulerian:
def test_is_semieulerian(self):
# Test graphs with Eulerian paths but no cycles return True.
assert nx.is_semieulerian(nx.path_graph(4))
G = nx.path_graph(6, create_using=nx.DiGraph)
assert nx.is_semieulerian(G)
# Test graphs with Eulerian cycles return False.
assert not nx.is_semieulerian(nx.complete_graph(5))
assert not nx.is_semieulerian(nx.complete_graph(7))
assert not nx.is_semieulerian(nx.hypercube_graph(4))
assert not nx.is_semieulerian(nx.hypercube_graph(6))
class TestHasEulerianPath:
def test_has_eulerian_path_cyclic(self):
# Test graphs with Eulerian cycles return True.
assert nx.has_eulerian_path(nx.complete_graph(5))
assert nx.has_eulerian_path(nx.complete_graph(7))
assert nx.has_eulerian_path(nx.hypercube_graph(4))
assert nx.has_eulerian_path(nx.hypercube_graph(6))
def test_has_eulerian_path_non_cyclic(self):
# Test graphs with Eulerian paths but no cycles return True.
assert nx.has_eulerian_path(nx.path_graph(4))
G = nx.path_graph(6, create_using=nx.DiGraph)
assert nx.has_eulerian_path(G)
class TestFindPathStart:
def testfind_path_start(self):
find_path_start = nx.algorithms.euler._find_path_start
# Test digraphs return correct starting node.
G = nx.path_graph(6, create_using=nx.DiGraph)
assert find_path_start(G) == 0
edges = [(0, 1), (1, 2), (2, 0), (4, 0)]
assert find_path_start(nx.DiGraph(edges)) == 4
# Test graph with no Eulerian path return None.
edges = [(0, 1), (1, 2), (2, 3), (2, 4)]
assert find_path_start(nx.DiGraph(edges)) is None
class TestEulerianPath:
def test_eulerian_path(self):
x = [(4, 0), (0, 1), (1, 2), (2, 0)]
for e1, e2 in zip(x, nx.eulerian_path(nx.DiGraph(x))):
assert e1 == e2
class TestEulerize:
def test_disconnected(self):
with pytest.raises(nx.NetworkXError):
G = nx.from_edgelist([(0, 1), (2, 3)])
nx.eulerize(G)
def test_null_graph(self):
with pytest.raises(nx.NetworkXPointlessConcept):
nx.eulerize(nx.Graph())
def test_null_multigraph(self):
with pytest.raises(nx.NetworkXPointlessConcept):
nx.eulerize(nx.MultiGraph())
def test_on_empty_graph(self):
with pytest.raises(nx.NetworkXError):
nx.eulerize(nx.empty_graph(3))
def test_on_eulerian(self):
G = nx.cycle_graph(3)
H = nx.eulerize(G)
assert nx.is_isomorphic(G, H)
def test_on_eulerian_multigraph(self):
G = nx.MultiGraph(nx.cycle_graph(3))
G.add_edge(0, 1)
H = nx.eulerize(G)
assert nx.is_eulerian(H)
def test_on_complete_graph(self):
G = nx.complete_graph(4)
assert nx.is_eulerian(nx.eulerize(G))
assert nx.is_eulerian(nx.eulerize(nx.MultiGraph(G)))

View file

@ -0,0 +1,42 @@
import networkx as nx
def test_empty_graph_hash():
G1 = nx.empty_graph()
G2 = nx.empty_graph()
h1 = nx.weisfeiler_lehman_graph_hash(G1)
h2 = nx.weisfeiler_lehman_graph_hash(G2)
assert h1 == h2
def test_relabel():
G1 = nx.Graph()
G1.add_edges_from(
[
(1, 2, {"label": "A"}),
(2, 3, {"label": "A"}),
(3, 1, {"label": "A"}),
(1, 4, {"label": "B"}),
]
)
h_before = nx.weisfeiler_lehman_graph_hash(G1, edge_attr="label")
G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
h_after = nx.weisfeiler_lehman_graph_hash(G2, edge_attr="label")
assert h_after == h_before
def test_directed():
G1 = nx.DiGraph()
G1.add_edges_from([(1, 2), (2, 3), (3, 1), (1, 5)])
h_directed = nx.weisfeiler_lehman_graph_hash(G1)
G2 = G1.to_undirected()
h_undirected = nx.weisfeiler_lehman_graph_hash(G2)
assert h_directed != h_undirected

View file

@ -0,0 +1,165 @@
import pytest
import networkx as nx
def test_valid_degree_sequence1():
n = 100
p = 0.3
for i in range(10):
G = nx.erdos_renyi_graph(n, p)
deg = (d for n, d in G.degree())
assert nx.is_graphical(deg, method="eg")
assert nx.is_graphical(deg, method="hh")
def test_valid_degree_sequence2():
n = 100
for i in range(10):
G = nx.barabasi_albert_graph(n, 1)
deg = (d for n, d in G.degree())
assert nx.is_graphical(deg, method="eg")
assert nx.is_graphical(deg, method="hh")
def test_string_input():
pytest.raises(nx.NetworkXException, nx.is_graphical, [], "foo")
pytest.raises(nx.NetworkXException, nx.is_graphical, ["red"], "hh")
pytest.raises(nx.NetworkXException, nx.is_graphical, ["red"], "eg")
def test_non_integer_input():
pytest.raises(nx.NetworkXException, nx.is_graphical, [72.5], "eg")
pytest.raises(nx.NetworkXException, nx.is_graphical, [72.5], "hh")
def test_negative_input():
assert not nx.is_graphical([-1], "hh")
assert not nx.is_graphical([-1], "eg")
class TestAtlas:
@classmethod
def setup_class(cls):
global atlas
# import platform
# if platform.python_implementation() == 'Jython':
# raise SkipTest('graph atlas not available under Jython.')
import networkx.generators.atlas as atlas
cls.GAG = atlas.graph_atlas_g()
def test_atlas(self):
for graph in self.GAG:
deg = (d for n, d in graph.degree())
assert nx.is_graphical(deg, method="eg")
assert nx.is_graphical(deg, method="hh")
def test_small_graph_true():
z = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
assert nx.is_graphical(z, method="hh")
assert nx.is_graphical(z, method="eg")
z = [10, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2]
assert nx.is_graphical(z, method="hh")
assert nx.is_graphical(z, method="eg")
z = [1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
assert nx.is_graphical(z, method="hh")
assert nx.is_graphical(z, method="eg")
def test_small_graph_false():
z = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
assert not nx.is_graphical(z, method="hh")
assert not nx.is_graphical(z, method="eg")
z = [6, 5, 4, 4, 2, 1, 1, 1]
assert not nx.is_graphical(z, method="hh")
assert not nx.is_graphical(z, method="eg")
z = [1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
assert not nx.is_graphical(z, method="hh")
assert not nx.is_graphical(z, method="eg")
def test_directed_degree_sequence():
# Test a range of valid directed degree sequences
n, r = 100, 10
p = 1.0 / r
for i in range(r):
G = nx.erdos_renyi_graph(n, p * (i + 1), None, True)
din = (d for n, d in G.in_degree())
dout = (d for n, d in G.out_degree())
assert nx.is_digraphical(din, dout)
def test_small_directed_sequences():
dout = [5, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
din = [3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1]
assert nx.is_digraphical(din, dout)
# Test nongraphical directed sequence
dout = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
din = [103, 102, 102, 102, 102, 102, 102, 102, 102, 102]
assert not nx.is_digraphical(din, dout)
# Test digraphical small sequence
dout = [1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
din = [2, 2, 2, 2, 2, 2, 2, 2, 1, 1]
assert nx.is_digraphical(din, dout)
# Test nonmatching sum
din = [2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1]
assert not nx.is_digraphical(din, dout)
# Test for negative integer in sequence
din = [2, 2, 2, -2, 2, 2, 2, 2, 1, 1, 4]
assert not nx.is_digraphical(din, dout)
# Test for noninteger
din = dout = [1, 1, 1.1, 1]
assert not nx.is_digraphical(din, dout)
din = dout = [1, 1, "rer", 1]
assert not nx.is_digraphical(din, dout)
def test_multi_sequence():
# Test nongraphical multi sequence
seq = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1]
assert not nx.is_multigraphical(seq)
# Test small graphical multi sequence
seq = [6, 5, 4, 4, 2, 1, 1, 1]
assert nx.is_multigraphical(seq)
# Test for negative integer in sequence
seq = [6, 5, 4, -4, 2, 1, 1, 1]
assert not nx.is_multigraphical(seq)
# Test for sequence with odd sum
seq = [1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4]
assert not nx.is_multigraphical(seq)
# Test for noninteger
seq = [1, 1, 1.1, 1]
assert not nx.is_multigraphical(seq)
seq = [1, 1, "rer", 1]
assert not nx.is_multigraphical(seq)
def test_pseudo_sequence():
# Test small valid pseudo sequence
seq = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1]
assert nx.is_pseudographical(seq)
# Test for sequence with odd sum
seq = [1000, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1]
assert not nx.is_pseudographical(seq)
# Test for negative integer in sequence
seq = [1000, 3, 3, 3, 3, 2, 2, -2, 1, 1]
assert not nx.is_pseudographical(seq)
# Test for noninteger
seq = [1, 1, 1.1, 1]
assert not nx.is_pseudographical(seq)
seq = [1, 1, "rer", 1]
assert not nx.is_pseudographical(seq)
def test_numpy_degree_sequence():
numpy = pytest.importorskip("numpy")
ds = numpy.array([1, 2, 2, 2, 1], dtype=numpy.int64)
assert nx.is_graphical(ds, "eg")
assert nx.is_graphical(ds, "hh")
ds = numpy.array([1, 2, 2, 2, 1], dtype=numpy.float64)
assert nx.is_graphical(ds, "eg")
assert nx.is_graphical(ds, "hh")
ds = numpy.array([1.1, 2, 2, 2, 1], dtype=numpy.float64)
pytest.raises(nx.NetworkXException, nx.is_graphical, ds, "eg")
pytest.raises(nx.NetworkXException, nx.is_graphical, ds, "hh")

View file

@ -0,0 +1,38 @@
import pytest
import networkx as nx
def test_hierarchy_exception():
G = nx.cycle_graph(5)
pytest.raises(nx.NetworkXError, nx.flow_hierarchy, G)
def test_hierarchy_cycle():
G = nx.cycle_graph(5, create_using=nx.DiGraph())
assert nx.flow_hierarchy(G) == 0.0
def test_hierarchy_tree():
G = nx.full_rary_tree(2, 16, create_using=nx.DiGraph())
assert nx.flow_hierarchy(G) == 1.0
def test_hierarchy_1():
G = nx.DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 1), (3, 4), (0, 4)])
assert nx.flow_hierarchy(G) == 0.5
def test_hierarchy_weight():
G = nx.DiGraph()
G.add_edges_from(
[
(0, 1, {"weight": 0.3}),
(1, 2, {"weight": 0.1}),
(2, 3, {"weight": 0.1}),
(3, 1, {"weight": 0.1}),
(3, 4, {"weight": 0.3}),
(0, 4, {"weight": 0.3}),
]
)
assert nx.flow_hierarchy(G, weight="weight") == 0.75

View file

@ -0,0 +1,24 @@
import networkx as nx
def test_2d_grid_graph():
# FC article claims 2d grid graph of size n is (3,3)-connected
# and (5,9)-connected, but I don't think it is (5,9)-connected
G = nx.grid_2d_graph(8, 8, periodic=True)
assert nx.is_kl_connected(G, 3, 3)
assert not nx.is_kl_connected(G, 5, 9)
(H, graphOK) = nx.kl_connected_subgraph(G, 5, 9, same_as_graph=True)
assert not graphOK
def test_small_graph():
G = nx.Graph()
G.add_edge(1, 2)
G.add_edge(1, 3)
G.add_edge(2, 3)
assert nx.is_kl_connected(G, 2, 2)
H = nx.kl_connected_subgraph(G, 2, 2)
(H, graphOK) = nx.kl_connected_subgraph(
G, 2, 2, low_memory=True, same_as_graph=True
)
assert graphOK

View file

@ -0,0 +1,26 @@
"""Unit tests for the :mod:`networkx.algorithms.isolates` module."""
import networkx as nx
def test_is_isolate():
G = nx.Graph()
G.add_edge(0, 1)
G.add_node(2)
assert not nx.is_isolate(G, 0)
assert not nx.is_isolate(G, 1)
assert nx.is_isolate(G, 2)
def test_isolates():
G = nx.Graph()
G.add_edge(0, 1)
G.add_nodes_from([2, 3])
assert sorted(nx.isolates(G)) == [2, 3]
def test_number_of_isolates():
G = nx.Graph()
G.add_edge(0, 1)
G.add_nodes_from([2, 3])
assert nx.number_of_isolates(G) == 2

View file

@ -0,0 +1,542 @@
import math
from functools import partial
import pytest
import networkx as nx
def _test_func(G, ebunch, expected, predict_func, **kwargs):
result = predict_func(G, ebunch, **kwargs)
exp_dict = {tuple(sorted([u, v])): score for u, v, score in expected}
res_dict = {tuple(sorted([u, v])): score for u, v, score in result}
assert len(exp_dict) == len(res_dict)
for p in exp_dict:
assert nx.testing.almost_equal(exp_dict[p], res_dict[p])
class TestResourceAllocationIndex:
@classmethod
def setup_class(cls):
cls.func = staticmethod(nx.resource_allocation_index)
cls.test = partial(_test_func, predict_func=cls.func)
def test_K5(self):
G = nx.complete_graph(5)
self.test(G, [(0, 1)], [(0, 1, 0.75)])
def test_P3(self):
G = nx.path_graph(3)
self.test(G, [(0, 2)], [(0, 2, 0.5)])
def test_S4(self):
G = nx.star_graph(4)
self.test(G, [(1, 2)], [(1, 2, 0.25)])
def test_notimplemented(self):
assert pytest.raises(
nx.NetworkXNotImplemented, self.func, nx.DiGraph([(0, 1), (1, 2)]), [(0, 2)]
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiDiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
def test_no_common_neighbor(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_equal_nodes(self):
G = nx.complete_graph(4)
self.test(G, [(0, 0)], [(0, 0, 1)])
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
self.test(G, None, [(0, 3, 0.5), (1, 2, 0.5), (1, 3, 0)])
class TestJaccardCoefficient:
@classmethod
def setup_class(cls):
cls.func = staticmethod(nx.jaccard_coefficient)
cls.test = partial(_test_func, predict_func=cls.func)
def test_K5(self):
G = nx.complete_graph(5)
self.test(G, [(0, 1)], [(0, 1, 0.6)])
def test_P4(self):
G = nx.path_graph(4)
self.test(G, [(0, 2)], [(0, 2, 0.5)])
def test_notimplemented(self):
assert pytest.raises(
nx.NetworkXNotImplemented, self.func, nx.DiGraph([(0, 1), (1, 2)]), [(0, 2)]
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiDiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
def test_no_common_neighbor(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (2, 3)])
self.test(G, [(0, 2)], [(0, 2, 0)])
def test_isolated_nodes(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
self.test(G, None, [(0, 3, 0.5), (1, 2, 0.5), (1, 3, 0)])
class TestAdamicAdarIndex:
@classmethod
def setup_class(cls):
cls.func = staticmethod(nx.adamic_adar_index)
cls.test = partial(_test_func, predict_func=cls.func)
def test_K5(self):
G = nx.complete_graph(5)
self.test(G, [(0, 1)], [(0, 1, 3 / math.log(4))])
def test_P3(self):
G = nx.path_graph(3)
self.test(G, [(0, 2)], [(0, 2, 1 / math.log(2))])
def test_S4(self):
G = nx.star_graph(4)
self.test(G, [(1, 2)], [(1, 2, 1 / math.log(4))])
def test_notimplemented(self):
assert pytest.raises(
nx.NetworkXNotImplemented, self.func, nx.DiGraph([(0, 1), (1, 2)]), [(0, 2)]
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiDiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
def test_no_common_neighbor(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_equal_nodes(self):
G = nx.complete_graph(4)
self.test(G, [(0, 0)], [(0, 0, 3 / math.log(3))])
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
self.test(
G, None, [(0, 3, 1 / math.log(2)), (1, 2, 1 / math.log(2)), (1, 3, 0)]
)
class TestPreferentialAttachment:
@classmethod
def setup_class(cls):
cls.func = staticmethod(nx.preferential_attachment)
cls.test = partial(_test_func, predict_func=cls.func)
def test_K5(self):
G = nx.complete_graph(5)
self.test(G, [(0, 1)], [(0, 1, 16)])
def test_P3(self):
G = nx.path_graph(3)
self.test(G, [(0, 1)], [(0, 1, 2)])
def test_S4(self):
G = nx.star_graph(4)
self.test(G, [(0, 2)], [(0, 2, 4)])
def test_notimplemented(self):
assert pytest.raises(
nx.NetworkXNotImplemented, self.func, nx.DiGraph([(0, 1), (1, 2)]), [(0, 2)]
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
assert pytest.raises(
nx.NetworkXNotImplemented,
self.func,
nx.MultiDiGraph([(0, 1), (1, 2)]),
[(0, 2)],
)
def test_zero_degrees(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
self.test(G, None, [(0, 3, 2), (1, 2, 2), (1, 3, 1)])
class TestCNSoundarajanHopcroft:
@classmethod
def setup_class(cls):
cls.func = staticmethod(nx.cn_soundarajan_hopcroft)
cls.test = partial(_test_func, predict_func=cls.func, community="community")
def test_K5(self):
G = nx.complete_graph(5)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 1
self.test(G, [(0, 1)], [(0, 1, 5)])
def test_P3(self):
G = nx.path_graph(3)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 0
self.test(G, [(0, 2)], [(0, 2, 1)])
def test_S4(self):
G = nx.star_graph(4)
G.nodes[0]["community"] = 1
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 1
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 0
self.test(G, [(1, 2)], [(1, 2, 2)])
def test_notimplemented(self):
G = nx.DiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
G = nx.MultiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
G = nx.MultiDiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
def test_no_common_neighbor(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_equal_nodes(self):
G = nx.complete_graph(3)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
self.test(G, [(0, 0)], [(0, 0, 4)])
def test_different_community(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 1
self.test(G, [(0, 3)], [(0, 3, 2)])
def test_no_community_information(self):
G = nx.complete_graph(5)
assert pytest.raises(nx.NetworkXAlgorithmError, list, self.func(G, [(0, 1)]))
def test_insufficient_community_information(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[3]["community"] = 0
assert pytest.raises(nx.NetworkXAlgorithmError, list, self.func(G, [(0, 3)]))
def test_sufficient_community_information(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (1, 2), (1, 3), (2, 4), (3, 4), (4, 5)])
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 0
self.test(G, [(1, 4)], [(1, 4, 4)])
def test_custom_community_attribute_name(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["cmty"] = 0
G.nodes[1]["cmty"] = 0
G.nodes[2]["cmty"] = 0
G.nodes[3]["cmty"] = 1
self.test(G, [(0, 3)], [(0, 3, 2)], community="cmty")
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
self.test(G, None, [(0, 3, 2), (1, 2, 1), (1, 3, 0)])
class TestRAIndexSoundarajanHopcroft:
@classmethod
def setup_class(cls):
cls.func = staticmethod(nx.ra_index_soundarajan_hopcroft)
cls.test = partial(_test_func, predict_func=cls.func, community="community")
def test_K5(self):
G = nx.complete_graph(5)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 1
self.test(G, [(0, 1)], [(0, 1, 0.5)])
def test_P3(self):
G = nx.path_graph(3)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 0
self.test(G, [(0, 2)], [(0, 2, 0)])
def test_S4(self):
G = nx.star_graph(4)
G.nodes[0]["community"] = 1
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 1
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 0
self.test(G, [(1, 2)], [(1, 2, 0.25)])
def test_notimplemented(self):
G = nx.DiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
G = nx.MultiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
G = nx.MultiDiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
def test_no_common_neighbor(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_equal_nodes(self):
G = nx.complete_graph(3)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
self.test(G, [(0, 0)], [(0, 0, 1)])
def test_different_community(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 1
self.test(G, [(0, 3)], [(0, 3, 0)])
def test_no_community_information(self):
G = nx.complete_graph(5)
assert pytest.raises(nx.NetworkXAlgorithmError, list, self.func(G, [(0, 1)]))
def test_insufficient_community_information(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[3]["community"] = 0
assert pytest.raises(nx.NetworkXAlgorithmError, list, self.func(G, [(0, 3)]))
def test_sufficient_community_information(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (1, 2), (1, 3), (2, 4), (3, 4), (4, 5)])
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 0
self.test(G, [(1, 4)], [(1, 4, 1)])
def test_custom_community_attribute_name(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["cmty"] = 0
G.nodes[1]["cmty"] = 0
G.nodes[2]["cmty"] = 0
G.nodes[3]["cmty"] = 1
self.test(G, [(0, 3)], [(0, 3, 0)], community="cmty")
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
self.test(G, None, [(0, 3, 0.5), (1, 2, 0), (1, 3, 0)])
class TestWithinInterCluster:
@classmethod
def setup_class(cls):
cls.delta = 0.001
cls.func = staticmethod(nx.within_inter_cluster)
cls.test = partial(
_test_func, predict_func=cls.func, delta=cls.delta, community="community"
)
def test_K5(self):
G = nx.complete_graph(5)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 1
self.test(G, [(0, 1)], [(0, 1, 2 / (1 + self.delta))])
def test_P3(self):
G = nx.path_graph(3)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 0
self.test(G, [(0, 2)], [(0, 2, 0)])
def test_S4(self):
G = nx.star_graph(4)
G.nodes[0]["community"] = 1
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 1
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 0
self.test(G, [(1, 2)], [(1, 2, 1 / self.delta)])
def test_notimplemented(self):
G = nx.DiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
G = nx.MultiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
G = nx.MultiDiGraph([(0, 1), (1, 2)])
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXNotImplemented, self.func, G, [(0, 2)])
def test_no_common_neighbor(self):
G = nx.Graph()
G.add_nodes_from([0, 1])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
self.test(G, [(0, 1)], [(0, 1, 0)])
def test_equal_nodes(self):
G = nx.complete_graph(3)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
self.test(G, [(0, 0)], [(0, 0, 2 / self.delta)])
def test_different_community(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 1
self.test(G, [(0, 3)], [(0, 3, 0)])
def test_no_inter_cluster_common_neighbor(self):
G = nx.complete_graph(4)
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
self.test(G, [(0, 3)], [(0, 3, 2 / self.delta)])
def test_no_community_information(self):
G = nx.complete_graph(5)
assert pytest.raises(nx.NetworkXAlgorithmError, list, self.func(G, [(0, 1)]))
def test_insufficient_community_information(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 0
G.nodes[3]["community"] = 0
assert pytest.raises(nx.NetworkXAlgorithmError, list, self.func(G, [(0, 3)]))
def test_sufficient_community_information(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (1, 2), (1, 3), (2, 4), (3, 4), (4, 5)])
G.nodes[1]["community"] = 0
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
G.nodes[4]["community"] = 0
self.test(G, [(1, 4)], [(1, 4, 2 / self.delta)])
def test_invalid_delta(self):
G = nx.complete_graph(3)
G.add_nodes_from([0, 1, 2], community=0)
assert pytest.raises(nx.NetworkXAlgorithmError, self.func, G, [(0, 1)], 0)
assert pytest.raises(nx.NetworkXAlgorithmError, self.func, G, [(0, 1)], -0.5)
def test_custom_community_attribute_name(self):
G = nx.complete_graph(4)
G.nodes[0]["cmty"] = 0
G.nodes[1]["cmty"] = 0
G.nodes[2]["cmty"] = 0
G.nodes[3]["cmty"] = 0
self.test(G, [(0, 3)], [(0, 3, 2 / self.delta)], community="cmty")
def test_all_nonexistent_edges(self):
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
G.nodes[0]["community"] = 0
G.nodes[1]["community"] = 1
G.nodes[2]["community"] = 0
G.nodes[3]["community"] = 0
self.test(G, None, [(0, 3, 1 / self.delta), (1, 2, 0), (1, 3, 0)])

View file

@ -0,0 +1,311 @@
import pytest
from itertools import chain, combinations, product
import networkx as nx
tree_all_pairs_lca = nx.tree_all_pairs_lowest_common_ancestor
all_pairs_lca = nx.all_pairs_lowest_common_ancestor
def get_pair(dictionary, n1, n2):
if (n1, n2) in dictionary:
return dictionary[n1, n2]
else:
return dictionary[n2, n1]
class TestTreeLCA:
@classmethod
def setup_class(cls):
cls.DG = nx.DiGraph()
edges = [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]
cls.DG.add_edges_from(edges)
cls.ans = dict(tree_all_pairs_lca(cls.DG, 0))
gold = {(n, n): n for n in cls.DG}
gold.update({(0, i): 0 for i in range(1, 7)})
gold.update(
{
(1, 2): 0,
(1, 3): 1,
(1, 4): 1,
(1, 5): 0,
(1, 6): 0,
(2, 3): 0,
(2, 4): 0,
(2, 5): 2,
(2, 6): 2,
(3, 4): 1,
(3, 5): 0,
(3, 6): 0,
(4, 5): 0,
(4, 6): 0,
(5, 6): 2,
}
)
cls.gold = gold
@staticmethod
def assert_has_same_pairs(d1, d2):
for (a, b) in ((min(pair), max(pair)) for pair in chain(d1, d2)):
assert get_pair(d1, a, b) == get_pair(d2, a, b)
def test_tree_all_pairs_lowest_common_ancestor1(self):
"""Specifying the root is optional."""
assert dict(tree_all_pairs_lca(self.DG)) == self.ans
def test_tree_all_pairs_lowest_common_ancestor2(self):
"""Specifying only some pairs gives only those pairs."""
test_pairs = [(0, 1), (0, 1), (1, 0)]
ans = dict(tree_all_pairs_lca(self.DG, 0, test_pairs))
assert (0, 1) in ans and (1, 0) in ans
assert len(ans) == 2
def test_tree_all_pairs_lowest_common_ancestor3(self):
"""Specifying no pairs same as specifying all."""
all_pairs = chain(combinations(self.DG, 2), ((node, node) for node in self.DG))
ans = dict(tree_all_pairs_lca(self.DG, 0, all_pairs))
self.assert_has_same_pairs(ans, self.ans)
def test_tree_all_pairs_lowest_common_ancestor4(self):
"""Gives the right answer."""
ans = dict(tree_all_pairs_lca(self.DG))
self.assert_has_same_pairs(self.gold, ans)
def test_tree_all_pairs_lowest_common_ancestor5(self):
"""Handles invalid input correctly."""
empty_digraph = tree_all_pairs_lca(nx.DiGraph())
pytest.raises(nx.NetworkXPointlessConcept, list, empty_digraph)
bad_pairs_digraph = tree_all_pairs_lca(self.DG, pairs=[(-1, -2)])
pytest.raises(nx.NodeNotFound, list, bad_pairs_digraph)
def test_tree_all_pairs_lowest_common_ancestor6(self):
"""Works on subtrees."""
ans = dict(tree_all_pairs_lca(self.DG, 1))
gold = {
pair: lca
for (pair, lca) in self.gold.items()
if all(n in (1, 3, 4) for n in pair)
}
self.assert_has_same_pairs(gold, ans)
def test_tree_all_pairs_lowest_common_ancestor7(self):
"""Works on disconnected nodes."""
G = nx.DiGraph()
G.add_node(1)
assert {(1, 1): 1} == dict(tree_all_pairs_lca(G))
G.add_node(0)
assert {(1, 1): 1} == dict(tree_all_pairs_lca(G, 1))
assert {(0, 0): 0} == dict(tree_all_pairs_lca(G, 0))
pytest.raises(nx.NetworkXError, list, tree_all_pairs_lca(G))
def test_tree_all_pairs_lowest_common_ancestor8(self):
"""Raises right errors if not a tree."""
# Cycle
G = nx.DiGraph([(1, 2), (2, 1)])
pytest.raises(nx.NetworkXError, list, tree_all_pairs_lca(G))
# DAG
G = nx.DiGraph([(0, 2), (1, 2)])
pytest.raises(nx.NetworkXError, list, tree_all_pairs_lca(G))
def test_tree_all_pairs_lowest_common_ancestor9(self):
"""Test that pairs works correctly as a generator."""
pairs = iter([(0, 1), (0, 1), (1, 0)])
some_pairs = dict(tree_all_pairs_lca(self.DG, 0, pairs))
assert (0, 1) in some_pairs and (1, 0) in some_pairs
assert len(some_pairs) == 2
def test_tree_all_pairs_lowest_common_ancestor10(self):
"""Test that pairs not in the graph raises error."""
lca = tree_all_pairs_lca(self.DG, 0, [(-1, -1)])
pytest.raises(nx.NodeNotFound, list, lca)
def test_tree_all_pairs_lowest_common_ancestor11(self):
"""Test that None as a node in the graph raises an error."""
G = nx.DiGraph([(None, 3)])
pytest.raises(nx.NetworkXError, list, tree_all_pairs_lca(G))
pytest.raises(
nx.NodeNotFound, list, tree_all_pairs_lca(self.DG, pairs=G.edges())
)
def test_tree_all_pairs_lowest_common_ancestor12(self):
"""Test that tree routine bails on DAGs."""
G = nx.DiGraph([(3, 4), (5, 4)])
pytest.raises(nx.NetworkXError, list, tree_all_pairs_lca(G))
def test_not_implemented_for(self):
NNI = nx.NetworkXNotImplemented
G = nx.Graph([(0, 1)])
pytest.raises(NNI, tree_all_pairs_lca, G)
pytest.raises(NNI, all_pairs_lca, G)
pytest.raises(NNI, nx.lowest_common_ancestor, G, 0, 1)
G = nx.MultiGraph([(0, 1)])
pytest.raises(NNI, tree_all_pairs_lca, G)
pytest.raises(NNI, all_pairs_lca, G)
pytest.raises(NNI, nx.lowest_common_ancestor, G, 0, 1)
G = nx.MultiDiGraph([(0, 1)])
pytest.raises(NNI, tree_all_pairs_lca, G)
pytest.raises(NNI, all_pairs_lca, G)
pytest.raises(NNI, nx.lowest_common_ancestor, G, 0, 1)
def test_tree_all_pairs_lowest_common_ancestor13(self):
"""Test that it works on non-empty trees with no LCAs."""
G = nx.DiGraph()
G.add_node(3)
ans = list(tree_all_pairs_lca(G))
assert ans == [((3, 3), 3)]
class TestDAGLCA:
@classmethod
def setup_class(cls):
cls.DG = nx.DiGraph()
nx.add_path(cls.DG, (0, 1, 2, 3))
nx.add_path(cls.DG, (0, 4, 3))
nx.add_path(cls.DG, (0, 5, 6, 8, 3))
nx.add_path(cls.DG, (5, 7, 8))
cls.DG.add_edge(6, 2)
cls.DG.add_edge(7, 2)
cls.root_distance = nx.shortest_path_length(cls.DG, source=0)
cls.gold = {
(1, 1): 1,
(1, 2): 1,
(1, 3): 1,
(1, 4): 0,
(1, 5): 0,
(1, 6): 0,
(1, 7): 0,
(1, 8): 0,
(2, 2): 2,
(2, 3): 2,
(2, 4): 0,
(2, 5): 5,
(2, 6): 6,
(2, 7): 7,
(2, 8): 7,
(3, 3): 8,
(3, 4): 4,
(3, 5): 5,
(3, 6): 6,
(3, 7): 7,
(3, 8): 8,
(4, 4): 4,
(4, 5): 0,
(4, 6): 0,
(4, 7): 0,
(4, 8): 0,
(5, 5): 5,
(5, 6): 5,
(5, 7): 5,
(5, 8): 5,
(6, 6): 6,
(6, 7): 5,
(6, 8): 6,
(7, 7): 7,
(7, 8): 7,
(8, 8): 8,
}
cls.gold.update(((0, n), 0) for n in cls.DG)
def assert_lca_dicts_same(self, d1, d2, G=None):
"""Checks if d1 and d2 contain the same pairs and
have a node at the same distance from root for each.
If G is None use self.DG."""
if G is None:
G = self.DG
root_distance = self.root_distance
else:
roots = [n for n, deg in G.in_degree if deg == 0]
assert len(roots) == 1
root_distance = nx.shortest_path_length(G, source=roots[0])
for a, b in ((min(pair), max(pair)) for pair in chain(d1, d2)):
assert (
root_distance[get_pair(d1, a, b)] == root_distance[get_pair(d2, a, b)]
)
def test_all_pairs_lowest_common_ancestor1(self):
"""Produces the correct results."""
self.assert_lca_dicts_same(dict(all_pairs_lca(self.DG)), self.gold)
def test_all_pairs_lowest_common_ancestor2(self):
"""Produces the correct results when all pairs given."""
all_pairs = list(product(self.DG.nodes(), self.DG.nodes()))
ans = all_pairs_lca(self.DG, pairs=all_pairs)
self.assert_lca_dicts_same(dict(ans), self.gold)
def test_all_pairs_lowest_common_ancestor3(self):
"""Produces the correct results when all pairs given as a generator."""
all_pairs = product(self.DG.nodes(), self.DG.nodes())
ans = all_pairs_lca(self.DG, pairs=all_pairs)
self.assert_lca_dicts_same(dict(ans), self.gold)
def test_all_pairs_lowest_common_ancestor4(self):
"""Graph with two roots."""
G = self.DG.copy()
G.add_edge(9, 10)
G.add_edge(9, 4)
gold = self.gold.copy()
gold[9, 9] = 9
gold[9, 10] = 9
gold[9, 4] = 9
gold[9, 3] = 9
gold[10, 4] = 9
gold[10, 3] = 9
gold[10, 10] = 10
testing = dict(all_pairs_lca(G))
G.add_edge(-1, 9)
G.add_edge(-1, 0)
self.assert_lca_dicts_same(testing, gold, G)
def test_all_pairs_lowest_common_ancestor5(self):
"""Test that pairs not in the graph raises error."""
pytest.raises(nx.NodeNotFound, all_pairs_lca, self.DG, [(-1, -1)])
def test_all_pairs_lowest_common_ancestor6(self):
"""Test that pairs with no LCA specified emits nothing."""
G = self.DG.copy()
G.add_node(-1)
gen = all_pairs_lca(G, [(-1, -1), (-1, 0)])
assert dict(gen) == {(-1, -1): -1}
def test_all_pairs_lowest_common_ancestor7(self):
"""Test that LCA on null graph bails."""
pytest.raises(nx.NetworkXPointlessConcept, all_pairs_lca, nx.DiGraph())
def test_all_pairs_lowest_common_ancestor8(self):
"""Test that LCA on non-dags bails."""
pytest.raises(nx.NetworkXError, all_pairs_lca, nx.DiGraph([(3, 4), (4, 3)]))
def test_all_pairs_lowest_common_ancestor9(self):
"""Test that it works on non-empty graphs with no LCAs."""
G = nx.DiGraph()
G.add_node(3)
ans = list(all_pairs_lca(G))
assert ans == [((3, 3), 3)]
def test_all_pairs_lowest_common_ancestor10(self):
"""Test that it bails on None as a node."""
G = nx.DiGraph([(None, 3)])
pytest.raises(nx.NetworkXError, all_pairs_lca, G)
pytest.raises(nx.NodeNotFound, all_pairs_lca, self.DG, pairs=G.edges())
def test_lowest_common_ancestor1(self):
"""Test that the one-pair function works on default."""
G = nx.DiGraph([(0, 1), (2, 1)])
sentinel = object()
assert nx.lowest_common_ancestor(G, 0, 2, default=sentinel) is sentinel
def test_lowest_common_ancestor2(self):
"""Test that the one-pair function works on identity."""
G = nx.DiGraph()
G.add_node(3)
assert nx.lowest_common_ancestor(G, 3, 3) == 3

View file

@ -0,0 +1,509 @@
from itertools import permutations
import math
import networkx as nx
from networkx.algorithms.matching import matching_dict_to_set
from networkx.testing import assert_edges_equal
class TestMaxWeightMatching:
"""Unit tests for the
:func:`~networkx.algorithms.matching.max_weight_matching` function.
"""
def test_trivial1(self):
"""Empty graph"""
G = nx.Graph()
assert nx.max_weight_matching(G) == set()
def test_trivial2(self):
"""Self loop"""
G = nx.Graph()
G.add_edge(0, 0, weight=100)
assert nx.max_weight_matching(G) == set()
def test_trivial3(self):
"""Single edge"""
G = nx.Graph()
G.add_edge(0, 1)
assert_edges_equal(
nx.max_weight_matching(G), matching_dict_to_set({0: 1, 1: 0})
)
def test_trivial4(self):
"""Small graph"""
G = nx.Graph()
G.add_edge("one", "two", weight=10)
G.add_edge("two", "three", weight=11)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({"three": "two", "two": "three"}),
)
def test_trivial5(self):
"""Path"""
G = nx.Graph()
G.add_edge(1, 2, weight=5)
G.add_edge(2, 3, weight=11)
G.add_edge(3, 4, weight=5)
assert_edges_equal(
nx.max_weight_matching(G), matching_dict_to_set({2: 3, 3: 2})
)
assert_edges_equal(
nx.max_weight_matching(G, 1), matching_dict_to_set({1: 2, 2: 1, 3: 4, 4: 3})
)
def test_trivial6(self):
"""Small graph with arbitrary weight attribute"""
G = nx.Graph()
G.add_edge("one", "two", weight=10, abcd=11)
G.add_edge("two", "three", weight=11, abcd=10)
assert_edges_equal(
nx.max_weight_matching(G, weight="abcd"),
matching_dict_to_set({"one": "two", "two": "one"}),
)
def test_floating_point_weights(self):
"""Floating point weights"""
G = nx.Graph()
G.add_edge(1, 2, weight=math.pi)
G.add_edge(2, 3, weight=math.exp(1))
G.add_edge(1, 3, weight=3.0)
G.add_edge(1, 4, weight=math.sqrt(2.0))
assert_edges_equal(
nx.max_weight_matching(G), matching_dict_to_set({1: 4, 2: 3, 3: 2, 4: 1})
)
def test_negative_weights(self):
"""Negative weights"""
G = nx.Graph()
G.add_edge(1, 2, weight=2)
G.add_edge(1, 3, weight=-2)
G.add_edge(2, 3, weight=1)
G.add_edge(2, 4, weight=-1)
G.add_edge(3, 4, weight=-6)
assert_edges_equal(
nx.max_weight_matching(G), matching_dict_to_set({1: 2, 2: 1})
)
assert_edges_equal(
nx.max_weight_matching(G, 1), matching_dict_to_set({1: 3, 2: 4, 3: 1, 4: 2})
)
def test_s_blossom(self):
"""Create S-blossom and use it for augmentation:"""
G = nx.Graph()
G.add_weighted_edges_from([(1, 2, 8), (1, 3, 9), (2, 3, 10), (3, 4, 7)])
assert_edges_equal(
nx.max_weight_matching(G), matching_dict_to_set({1: 2, 2: 1, 3: 4, 4: 3})
)
G.add_weighted_edges_from([(1, 6, 5), (4, 5, 6)])
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1}),
)
def test_s_t_blossom(self):
"""Create S-blossom, relabel as T-blossom, use for augmentation:"""
G = nx.Graph()
G.add_weighted_edges_from(
[(1, 2, 9), (1, 3, 8), (2, 3, 10), (1, 4, 5), (4, 5, 4), (1, 6, 3)]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1}),
)
G.add_edge(4, 5, weight=3)
G.add_edge(1, 6, weight=4)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 5, 5: 4, 6: 1}),
)
G.remove_edge(1, 6)
G.add_edge(3, 6, weight=4)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 2, 2: 1, 3: 6, 4: 5, 5: 4, 6: 3}),
)
def test_nested_s_blossom(self):
"""Create nested S-blossom, use for augmentation:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 9),
(1, 3, 9),
(2, 3, 10),
(2, 4, 8),
(3, 5, 8),
(4, 5, 10),
(5, 6, 6),
]
)
dict_format = {1: 3, 2: 4, 3: 1, 4: 2, 5: 6, 6: 5}
expected = {frozenset(e) for e in matching_dict_to_set(dict_format)}
answer = {frozenset(e) for e in nx.max_weight_matching(G)}
assert answer == expected
def test_nested_s_blossom_relabel(self):
"""Create S-blossom, relabel as S, include in nested S-blossom:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 10),
(1, 7, 10),
(2, 3, 12),
(3, 4, 20),
(3, 5, 20),
(4, 5, 25),
(5, 6, 10),
(6, 7, 10),
(7, 8, 8),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 2, 2: 1, 3: 4, 4: 3, 5: 6, 6: 5, 7: 8, 8: 7}),
)
def test_nested_s_blossom_expand(self):
"""Create nested S-blossom, augment, expand recursively:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 8),
(1, 3, 8),
(2, 3, 10),
(2, 4, 12),
(3, 5, 12),
(4, 5, 14),
(4, 6, 12),
(5, 7, 12),
(6, 7, 14),
(7, 8, 12),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 2, 2: 1, 3: 5, 4: 6, 5: 3, 6: 4, 7: 8, 8: 7}),
)
def test_s_blossom_relabel_expand(self):
"""Create S-blossom, relabel as T, expand:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 23),
(1, 5, 22),
(1, 6, 15),
(2, 3, 25),
(3, 4, 22),
(4, 5, 25),
(4, 8, 14),
(5, 7, 13),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4}),
)
def test_nested_s_blossom_relabel_expand(self):
"""Create nested S-blossom, relabel as T, expand:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 19),
(1, 3, 20),
(1, 8, 8),
(2, 3, 25),
(2, 4, 18),
(3, 5, 18),
(4, 5, 13),
(4, 7, 7),
(5, 6, 7),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set({1: 8, 2: 3, 3: 2, 4: 7, 5: 6, 6: 5, 7: 4, 8: 1}),
)
def test_nasty_blossom1(self):
"""Create blossom, relabel as T in more than one way, expand,
augment:
"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 45),
(1, 5, 45),
(2, 3, 50),
(3, 4, 45),
(4, 5, 50),
(1, 6, 30),
(3, 9, 35),
(4, 8, 35),
(5, 7, 26),
(9, 10, 5),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set(
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4, 9: 10, 10: 9}
),
)
def test_nasty_blossom2(self):
"""Again but slightly different:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 45),
(1, 5, 45),
(2, 3, 50),
(3, 4, 45),
(4, 5, 50),
(1, 6, 30),
(3, 9, 35),
(4, 8, 26),
(5, 7, 40),
(9, 10, 5),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set(
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4, 9: 10, 10: 9}
),
)
def test_nasty_blossom_least_slack(self):
"""Create blossom, relabel as T, expand such that a new
least-slack S-to-free dge is produced, augment:
"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 45),
(1, 5, 45),
(2, 3, 50),
(3, 4, 45),
(4, 5, 50),
(1, 6, 30),
(3, 9, 35),
(4, 8, 28),
(5, 7, 26),
(9, 10, 5),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set(
{1: 6, 2: 3, 3: 2, 4: 8, 5: 7, 6: 1, 7: 5, 8: 4, 9: 10, 10: 9}
),
)
def test_nasty_blossom_augmenting(self):
"""Create nested blossom, relabel as T in more than one way"""
# expand outer blossom such that inner blossom ends up on an
# augmenting path:
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 45),
(1, 7, 45),
(2, 3, 50),
(3, 4, 45),
(4, 5, 95),
(4, 6, 94),
(5, 6, 94),
(6, 7, 50),
(1, 8, 30),
(3, 11, 35),
(5, 9, 36),
(7, 10, 26),
(11, 12, 5),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set(
{
1: 8,
2: 3,
3: 2,
4: 6,
5: 9,
6: 4,
7: 10,
8: 1,
9: 5,
10: 7,
11: 12,
12: 11,
}
),
)
def test_nasty_blossom_expand_recursively(self):
"""Create nested S-blossom, relabel as S, expand recursively:"""
G = nx.Graph()
G.add_weighted_edges_from(
[
(1, 2, 40),
(1, 3, 40),
(2, 3, 60),
(2, 4, 55),
(3, 5, 55),
(4, 5, 50),
(1, 8, 15),
(5, 7, 30),
(7, 6, 10),
(8, 10, 10),
(4, 9, 30),
]
)
assert_edges_equal(
nx.max_weight_matching(G),
matching_dict_to_set(
{1: 2, 2: 1, 3: 5, 4: 9, 5: 3, 6: 7, 7: 6, 8: 10, 9: 4, 10: 8}
),
)
class TestIsMatching:
"""Unit tests for the
:func:`~networkx.algorithms.matching.is_matching` function.
"""
def test_dict(self):
G = nx.path_graph(4)
assert nx.is_matching(G, {0: 1, 1: 0, 2: 3, 3: 2})
def test_empty_matching(self):
G = nx.path_graph(4)
assert nx.is_matching(G, set())
def test_single_edge(self):
G = nx.path_graph(4)
assert nx.is_matching(G, {(1, 2)})
def test_edge_order(self):
G = nx.path_graph(4)
assert nx.is_matching(G, {(0, 1), (2, 3)})
assert nx.is_matching(G, {(1, 0), (2, 3)})
assert nx.is_matching(G, {(0, 1), (3, 2)})
assert nx.is_matching(G, {(1, 0), (3, 2)})
def test_valid(self):
G = nx.path_graph(4)
assert nx.is_matching(G, {(0, 1), (2, 3)})
def test_invalid(self):
G = nx.path_graph(4)
assert not nx.is_matching(G, {(0, 1), (1, 2), (2, 3)})
class TestIsMaximalMatching:
"""Unit tests for the
:func:`~networkx.algorithms.matching.is_maximal_matching` function.
"""
def test_dict(self):
G = nx.path_graph(4)
assert nx.is_maximal_matching(G, {0: 1, 1: 0, 2: 3, 3: 2})
def test_valid(self):
G = nx.path_graph(4)
assert nx.is_maximal_matching(G, {(0, 1), (2, 3)})
def test_not_matching(self):
G = nx.path_graph(4)
assert not nx.is_maximal_matching(G, {(0, 1), (1, 2), (2, 3)})
def test_not_maximal(self):
G = nx.path_graph(4)
assert not nx.is_maximal_matching(G, {(0, 1)})
class TestIsPerfectMatching:
"""Unit tests for the
:func:`~networkx.algorithms.matching.is_perfect_matching` function.
"""
def test_dict(self):
G = nx.path_graph(4)
assert nx.is_perfect_matching(G, {0: 1, 1: 0, 2: 3, 3: 2})
def test_valid(self):
G = nx.path_graph(4)
assert nx.is_perfect_matching(G, {(0, 1), (2, 3)})
def test_valid_not_path(self):
G = nx.cycle_graph(4)
G.add_edge(0, 4)
G.add_edge(1, 4)
G.add_edge(5, 2)
assert nx.is_perfect_matching(G, {(1, 4), (0, 3), (5, 2)})
def test_not_matching(self):
G = nx.path_graph(4)
assert not nx.is_perfect_matching(G, {(0, 1), (1, 2), (2, 3)})
def test_maximal_but_not_perfect(self):
G = nx.cycle_graph(4)
G.add_edge(0, 4)
G.add_edge(1, 4)
assert not nx.is_perfect_matching(G, {(1, 4), (0, 3)})
class TestMaximalMatching:
"""Unit tests for the
:func:`~networkx.algorithms.matching.maximal_matching`.
"""
def test_valid_matching(self):
edges = [(1, 2), (1, 5), (2, 3), (2, 5), (3, 4), (3, 6), (5, 6)]
G = nx.Graph(edges)
matching = nx.maximal_matching(G)
assert nx.is_maximal_matching(G, matching)
def test_single_edge_matching(self):
# In the star graph, any maximal matching has just one edge.
G = nx.star_graph(5)
matching = nx.maximal_matching(G)
assert 1 == len(matching)
assert nx.is_maximal_matching(G, matching)
def test_self_loops(self):
# Create the path graph with two self-loops.
G = nx.path_graph(3)
G.add_edges_from([(0, 0), (1, 1)])
matching = nx.maximal_matching(G)
assert len(matching) == 1
# The matching should never include self-loops.
assert not any(u == v for u, v in matching)
assert nx.is_maximal_matching(G, matching)
def test_ordering(self):
"""Tests that a maximal matching is computed correctly
regardless of the order in which nodes are added to the graph.
"""
for nodes in permutations(range(3)):
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from([(0, 1), (0, 2)])
matching = nx.maximal_matching(G)
assert len(matching) == 1
assert nx.is_maximal_matching(G, matching)

View file

@ -0,0 +1,180 @@
"""Maximum weight clique test suite.
"""
import networkx as nx
import pytest
class TestMaximumWeightClique:
def test_basic_cases(self):
def check_basic_case(graph_func, expected_weight, weight_accessor):
graph = graph_func()
clique, weight = nx.algorithms.max_weight_clique(graph, weight_accessor)
assert verify_clique(
graph, clique, weight, expected_weight, weight_accessor
)
for graph_func, (expected_weight, expected_size) in TEST_CASES.items():
check_basic_case(graph_func, expected_weight, "weight")
check_basic_case(graph_func, expected_size, None)
def test_key_error(self):
graph = two_node_graph()
with pytest.raises(KeyError):
nx.algorithms.max_weight_clique(graph, "non-existent-key")
def test_error_on_non_integer_weight(self):
graph = two_node_graph()
graph.nodes[2]["weight"] = 1.5
with pytest.raises(ValueError):
nx.algorithms.max_weight_clique(graph)
def test_unaffected_by_self_loops(self):
graph = two_node_graph()
graph.add_edge(1, 1)
graph.add_edge(2, 2)
clique, weight = nx.algorithms.max_weight_clique(graph, "weight")
assert verify_clique(graph, clique, weight, 30, "weight")
graph = three_node_independent_set()
graph.add_edge(1, 1)
clique, weight = nx.algorithms.max_weight_clique(graph, "weight")
assert verify_clique(graph, clique, weight, 20, "weight")
def test_30_node_prob(self):
G = nx.Graph()
G.add_nodes_from(range(1, 31))
for i in range(1, 31):
G.nodes[i]["weight"] = i + 1
# fmt: off
G.add_edges_from(
[
(1, 12), (1, 13), (1, 15), (1, 16), (1, 18), (1, 19), (1, 20),
(1, 23), (1, 26), (1, 28), (1, 29), (1, 30), (2, 3), (2, 4),
(2, 5), (2, 8), (2, 9), (2, 10), (2, 14), (2, 17), (2, 18),
(2, 21), (2, 22), (2, 23), (2, 27), (3, 9), (3, 15), (3, 21),
(3, 22), (3, 23), (3, 24), (3, 27), (3, 28), (3, 29), (4, 5),
(4, 6), (4, 8), (4, 21), (4, 22), (4, 23), (4, 26), (4, 28),
(4, 30), (5, 6), (5, 8), (5, 9), (5, 13), (5, 14), (5, 15),
(5, 16), (5, 20), (5, 21), (5, 22), (5, 25), (5, 28), (5, 29),
(6, 7), (6, 8), (6, 13), (6, 17), (6, 18), (6, 19), (6, 24),
(6, 26), (6, 27), (6, 28), (6, 29), (7, 12), (7, 14), (7, 15),
(7, 16), (7, 17), (7, 20), (7, 25), (7, 27), (7, 29), (7, 30),
(8, 10), (8, 15), (8, 16), (8, 18), (8, 20), (8, 22), (8, 24),
(8, 26), (8, 27), (8, 28), (8, 30), (9, 11), (9, 12), (9, 13),
(9, 14), (9, 15), (9, 16), (9, 19), (9, 20), (9, 21), (9, 24),
(9, 30), (10, 12), (10, 15), (10, 18), (10, 19), (10, 20),
(10, 22), (10, 23), (10, 24), (10, 26), (10, 27), (10, 29),
(10, 30), (11, 13), (11, 15), (11, 16), (11, 17), (11, 18),
(11, 19), (11, 20), (11, 22), (11, 29), (11, 30), (12, 14),
(12, 17), (12, 18), (12, 19), (12, 20), (12, 21), (12, 23),
(12, 25), (12, 26), (12, 30), (13, 20), (13, 22), (13, 23),
(13, 24), (13, 30), (14, 16), (14, 20), (14, 21), (14, 22),
(14, 23), (14, 25), (14, 26), (14, 27), (14, 29), (14, 30),
(15, 17), (15, 18), (15, 20), (15, 21), (15, 26), (15, 27),
(15, 28), (16, 17), (16, 18), (16, 19), (16, 20), (16, 21),
(16, 29), (16, 30), (17, 18), (17, 21), (17, 22), (17, 25),
(17, 27), (17, 28), (17, 30), (18, 19), (18, 20), (18, 21),
(18, 22), (18, 23), (18, 24), (19, 20), (19, 22), (19, 23),
(19, 24), (19, 25), (19, 27), (19, 30), (20, 21), (20, 23),
(20, 24), (20, 26), (20, 28), (20, 29), (21, 23), (21, 26),
(21, 27), (21, 29), (22, 24), (22, 25), (22, 26), (22, 29),
(23, 25), (23, 30), (24, 25), (24, 26), (25, 27), (25, 29),
(26, 27), (26, 28), (26, 30), (28, 29), (29, 30),
]
)
# fmt: on
clique, weight = nx.algorithms.max_weight_clique(G)
assert verify_clique(G, clique, weight, 111, "weight")
# ############################ Utility functions ############################
def verify_clique(
graph, clique, reported_clique_weight, expected_clique_weight, weight_accessor
):
for node1 in clique:
for node2 in clique:
if node1 == node2:
continue
if not graph.has_edge(node1, node2):
return False
if weight_accessor is None:
clique_weight = len(clique)
else:
clique_weight = sum(graph.nodes[v]["weight"] for v in clique)
if clique_weight != expected_clique_weight:
return False
if clique_weight != reported_clique_weight:
return False
return True
# ############################ Graph Generation ############################
def empty_graph():
return nx.Graph()
def one_node_graph():
graph = nx.Graph()
graph.add_nodes_from([1])
graph.nodes[1]["weight"] = 10
return graph
def two_node_graph():
graph = nx.Graph()
graph.add_nodes_from([1, 2])
graph.add_edges_from([(1, 2)])
graph.nodes[1]["weight"] = 10
graph.nodes[2]["weight"] = 20
return graph
def three_node_clique():
graph = nx.Graph()
graph.add_nodes_from([1, 2, 3])
graph.add_edges_from([(1, 2), (1, 3), (2, 3)])
graph.nodes[1]["weight"] = 10
graph.nodes[2]["weight"] = 20
graph.nodes[3]["weight"] = 5
return graph
def three_node_independent_set():
graph = nx.Graph()
graph.add_nodes_from([1, 2, 3])
graph.nodes[1]["weight"] = 10
graph.nodes[2]["weight"] = 20
graph.nodes[3]["weight"] = 5
return graph
def disconnected():
graph = nx.Graph()
graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6)])
graph.nodes[1]["weight"] = 10
graph.nodes[2]["weight"] = 20
graph.nodes[3]["weight"] = 5
graph.nodes[4]["weight"] = 100
graph.nodes[5]["weight"] = 200
graph.nodes[6]["weight"] = 50
return graph
# --------------------------------------------------------------------------
# Basic tests for all strategies
# For each basic graph function, specify expected weight of max weight clique
# and expected size of maximum clique
TEST_CASES = {
empty_graph: (0, 0),
one_node_graph: (10, 1),
two_node_graph: (30, 2),
three_node_clique: (35, 3),
three_node_independent_set: (20, 1),
disconnected: (300, 2),
}

View file

@ -0,0 +1,364 @@
"""Unit tests for the :mod:`networkx.algorithms.minors` module."""
import pytest
import networkx as nx
from networkx.testing.utils import assert_edges_equal, assert_nodes_equal
from networkx.utils import arbitrary_element
class TestQuotient:
"""Unit tests for computing quotient graphs."""
def test_quotient_graph_complete_multipartite(self):
"""Tests that the quotient graph of the complete *n*-partite graph
under the "same neighbors" node relation is the complete graph on *n*
nodes.
"""
G = nx.complete_multipartite_graph(2, 3, 4)
# Two nodes are equivalent if they are not adjacent but have the same
# neighbor set.
def same_neighbors(u, v):
return u not in G[v] and v not in G[u] and G[u] == G[v]
expected = nx.complete_graph(3)
actual = nx.quotient_graph(G, same_neighbors)
# It won't take too long to run a graph isomorphism algorithm on such
# small graphs.
assert nx.is_isomorphic(expected, actual)
def test_quotient_graph_complete_bipartite(self):
"""Tests that the quotient graph of the complete bipartite graph under
the "same neighbors" node relation is `K_2`.
"""
G = nx.complete_bipartite_graph(2, 3)
# Two nodes are equivalent if they are not adjacent but have the same
# neighbor set.
def same_neighbors(u, v):
return u not in G[v] and v not in G[u] and G[u] == G[v]
expected = nx.complete_graph(2)
actual = nx.quotient_graph(G, same_neighbors)
# It won't take too long to run a graph isomorphism algorithm on such
# small graphs.
assert nx.is_isomorphic(expected, actual)
def test_quotient_graph_edge_relation(self):
"""Tests for specifying an alternate edge relation for the quotient
graph.
"""
G = nx.path_graph(5)
def identity(u, v):
return u == v
def same_parity(b, c):
return arbitrary_element(b) % 2 == arbitrary_element(c) % 2
actual = nx.quotient_graph(G, identity, same_parity)
expected = nx.Graph()
expected.add_edges_from([(0, 2), (0, 4), (2, 4)])
expected.add_edge(1, 3)
assert nx.is_isomorphic(actual, expected)
def test_condensation_as_quotient(self):
"""This tests that the condensation of a graph can be viewed as the
quotient graph under the "in the same connected component" equivalence
relation.
"""
# This example graph comes from the file `test_strongly_connected.py`.
G = nx.DiGraph()
G.add_edges_from(
[
(1, 2),
(2, 3),
(2, 11),
(2, 12),
(3, 4),
(4, 3),
(4, 5),
(5, 6),
(6, 5),
(6, 7),
(7, 8),
(7, 9),
(7, 10),
(8, 9),
(9, 7),
(10, 6),
(11, 2),
(11, 4),
(11, 6),
(12, 6),
(12, 11),
]
)
scc = list(nx.strongly_connected_components(G))
C = nx.condensation(G, scc)
component_of = C.graph["mapping"]
# Two nodes are equivalent if they are in the same connected component.
def same_component(u, v):
return component_of[u] == component_of[v]
Q = nx.quotient_graph(G, same_component)
assert nx.is_isomorphic(C, Q)
def test_path(self):
G = nx.path_graph(6)
partition = [{0, 1}, {2, 3}, {4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
for n in M:
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 1
def test_multigraph_path(self):
G = nx.MultiGraph(nx.path_graph(6))
partition = [{0, 1}, {2, 3}, {4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
for n in M:
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 1
def test_directed_path(self):
G = nx.DiGraph()
nx.add_path(G, range(6))
partition = [{0, 1}, {2, 3}, {4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
for n in M:
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 0.5
def test_directed_multigraph_path(self):
G = nx.MultiDiGraph()
nx.add_path(G, range(6))
partition = [{0, 1}, {2, 3}, {4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
for n in M:
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 0.5
def test_overlapping_blocks(self):
with pytest.raises(nx.NetworkXException):
G = nx.path_graph(6)
partition = [{0, 1, 2}, {2, 3}, {4, 5}]
nx.quotient_graph(G, partition)
def test_weighted_path(self):
G = nx.path_graph(6)
for i in range(5):
G[i][i + 1]["weight"] = i + 1
partition = [{0, 1}, {2, 3}, {4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
assert M[0][1]["weight"] == 2
assert M[1][2]["weight"] == 4
for n in M:
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 1
def test_barbell(self):
G = nx.barbell_graph(3, 0)
partition = [{0, 1, 2}, {3, 4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1])
assert_edges_equal(M.edges(), [(0, 1)])
for n in M:
assert M.nodes[n]["nedges"] == 3
assert M.nodes[n]["nnodes"] == 3
assert M.nodes[n]["density"] == 1
def test_barbell_plus(self):
G = nx.barbell_graph(3, 0)
# Add an extra edge joining the bells.
G.add_edge(0, 5)
partition = [{0, 1, 2}, {3, 4, 5}]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M, [0, 1])
assert_edges_equal(M.edges(), [(0, 1)])
assert M[0][1]["weight"] == 2
for n in M:
assert M.nodes[n]["nedges"] == 3
assert M.nodes[n]["nnodes"] == 3
assert M.nodes[n]["density"] == 1
def test_blockmodel(self):
G = nx.path_graph(6)
partition = [[0, 1], [2, 3], [4, 5]]
M = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(M.nodes(), [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
for n in M.nodes():
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 1.0
def test_multigraph_blockmodel(self):
G = nx.MultiGraph(nx.path_graph(6))
partition = [[0, 1], [2, 3], [4, 5]]
M = nx.quotient_graph(G, partition, create_using=nx.MultiGraph(), relabel=True)
assert_nodes_equal(M.nodes(), [0, 1, 2])
assert_edges_equal(M.edges(), [(0, 1), (1, 2)])
for n in M.nodes():
assert M.nodes[n]["nedges"] == 1
assert M.nodes[n]["nnodes"] == 2
assert M.nodes[n]["density"] == 1.0
def test_quotient_graph_incomplete_partition(self):
G = nx.path_graph(6)
partition = []
H = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(H.nodes(), [])
assert_edges_equal(H.edges(), [])
partition = [[0, 1], [2, 3], [5]]
H = nx.quotient_graph(G, partition, relabel=True)
assert_nodes_equal(H.nodes(), [0, 1, 2])
assert_edges_equal(H.edges(), [(0, 1)])
class TestContraction:
"""Unit tests for node and edge contraction functions."""
def test_undirected_node_contraction(self):
"""Tests for node contraction in an undirected graph."""
G = nx.cycle_graph(4)
actual = nx.contracted_nodes(G, 0, 1)
expected = nx.cycle_graph(3)
expected.add_edge(0, 0)
assert nx.is_isomorphic(actual, expected)
def test_directed_node_contraction(self):
"""Tests for node contraction in a directed graph."""
G = nx.DiGraph(nx.cycle_graph(4))
actual = nx.contracted_nodes(G, 0, 1)
expected = nx.DiGraph(nx.cycle_graph(3))
expected.add_edge(0, 0)
expected.add_edge(0, 0)
assert nx.is_isomorphic(actual, expected)
def test_undirected_node_contraction_no_copy(self):
"""Tests for node contraction in an undirected graph
by making changes in place."""
G = nx.cycle_graph(4)
actual = nx.contracted_nodes(G, 0, 1, copy=False)
expected = nx.cycle_graph(3)
expected.add_edge(0, 0)
assert nx.is_isomorphic(actual, G)
assert nx.is_isomorphic(actual, expected)
def test_directed_node_contraction_no_copy(self):
"""Tests for node contraction in a directed graph
by making changes in place."""
G = nx.DiGraph(nx.cycle_graph(4))
actual = nx.contracted_nodes(G, 0, 1, copy=False)
expected = nx.DiGraph(nx.cycle_graph(3))
expected.add_edge(0, 0)
expected.add_edge(0, 0)
assert nx.is_isomorphic(actual, G)
assert nx.is_isomorphic(actual, expected)
def test_create_multigraph(self):
"""Tests that using a MultiGraph creates multiple edges."""
G = nx.path_graph(3, create_using=nx.MultiGraph())
G.add_edge(0, 1)
G.add_edge(0, 0)
G.add_edge(0, 2)
actual = nx.contracted_nodes(G, 0, 2)
expected = nx.MultiGraph()
expected.add_edge(0, 1)
expected.add_edge(0, 1)
expected.add_edge(0, 1)
expected.add_edge(0, 0)
expected.add_edge(0, 0)
assert_edges_equal(actual.edges, expected.edges)
def test_multigraph_keys(self):
"""Tests that multiedge keys are reset in new graph."""
G = nx.path_graph(3, create_using=nx.MultiGraph())
G.add_edge(0, 1, 5)
G.add_edge(0, 0, 0)
G.add_edge(0, 2, 5)
actual = nx.contracted_nodes(G, 0, 2)
expected = nx.MultiGraph()
expected.add_edge(0, 1, 0)
expected.add_edge(0, 1, 5)
expected.add_edge(0, 1, 2) # keyed as 2 b/c 2 edges already in G
expected.add_edge(0, 0, 0)
expected.add_edge(0, 0, 1) # this comes from (0, 2, 5)
assert_edges_equal(actual.edges, expected.edges)
def test_node_attributes(self):
"""Tests that node contraction preserves node attributes."""
G = nx.cycle_graph(4)
# Add some data to the two nodes being contracted.
G.nodes[0]["foo"] = "bar"
G.nodes[1]["baz"] = "xyzzy"
actual = nx.contracted_nodes(G, 0, 1)
# We expect that contracting the nodes 0 and 1 in C_4 yields K_3, but
# with nodes labeled 0, 2, and 3, and with a self-loop on 0.
expected = nx.complete_graph(3)
expected = nx.relabel_nodes(expected, {1: 2, 2: 3})
expected.add_edge(0, 0)
cdict = {1: {"baz": "xyzzy"}}
expected.nodes[0].update(dict(foo="bar", contraction=cdict))
assert nx.is_isomorphic(actual, expected)
assert actual.nodes == expected.nodes
def test_without_self_loops(self):
"""Tests for node contraction without preserving self-loops."""
G = nx.cycle_graph(4)
actual = nx.contracted_nodes(G, 0, 1, self_loops=False)
expected = nx.complete_graph(3)
assert nx.is_isomorphic(actual, expected)
def test_contract_selfloop_graph(self):
"""Tests for node contraction when nodes have selfloops."""
G = nx.cycle_graph(4)
G.add_edge(0, 0)
actual = nx.contracted_nodes(G, 0, 1)
expected = nx.complete_graph([0, 2, 3])
expected.add_edge(0, 0)
expected.add_edge(0, 0)
assert_edges_equal(actual.edges, expected.edges)
actual = nx.contracted_nodes(G, 1, 0)
expected = nx.complete_graph([1, 2, 3])
expected.add_edge(1, 1)
expected.add_edge(1, 1)
assert_edges_equal(actual.edges, expected.edges)
def test_undirected_edge_contraction(self):
"""Tests for edge contraction in an undirected graph."""
G = nx.cycle_graph(4)
actual = nx.contracted_edge(G, (0, 1))
expected = nx.complete_graph(3)
expected.add_edge(0, 0)
assert nx.is_isomorphic(actual, expected)
def test_nonexistent_edge(self):
"""Tests that attempting to contract a non-existent edge raises an
exception.
"""
with pytest.raises(ValueError):
G = nx.cycle_graph(4)
nx.contracted_edge(G, (0, 2))

View file

@ -0,0 +1,85 @@
"""
Tests for maximal (not maximum) independent sets.
"""
import pytest
import networkx as nx
import random
class TestMaximalIndependantSet:
def setup(self):
self.florentine = nx.Graph()
self.florentine.add_edge("Acciaiuoli", "Medici")
self.florentine.add_edge("Castellani", "Peruzzi")
self.florentine.add_edge("Castellani", "Strozzi")
self.florentine.add_edge("Castellani", "Barbadori")
self.florentine.add_edge("Medici", "Barbadori")
self.florentine.add_edge("Medici", "Ridolfi")
self.florentine.add_edge("Medici", "Tornabuoni")
self.florentine.add_edge("Medici", "Albizzi")
self.florentine.add_edge("Medici", "Salviati")
self.florentine.add_edge("Salviati", "Pazzi")
self.florentine.add_edge("Peruzzi", "Strozzi")
self.florentine.add_edge("Peruzzi", "Bischeri")
self.florentine.add_edge("Strozzi", "Ridolfi")
self.florentine.add_edge("Strozzi", "Bischeri")
self.florentine.add_edge("Ridolfi", "Tornabuoni")
self.florentine.add_edge("Tornabuoni", "Guadagni")
self.florentine.add_edge("Albizzi", "Ginori")
self.florentine.add_edge("Albizzi", "Guadagni")
self.florentine.add_edge("Bischeri", "Guadagni")
self.florentine.add_edge("Guadagni", "Lamberteschi")
def test_random_seed(self):
G = nx.complete_graph(5)
for node in G:
assert nx.maximal_independent_set(G, [node], seed=1) == [node]
def test_K5(self):
"""Maximal independent set: K5"""
G = nx.complete_graph(5)
for node in G:
assert nx.maximal_independent_set(G, [node]) == [node]
def test_K55(self):
"""Maximal independent set: K55"""
G = nx.complete_graph(55)
for node in G:
assert nx.maximal_independent_set(G, [node]) == [node]
def test_exception(self):
"""Bad input should raise exception."""
G = self.florentine
pytest.raises(nx.NetworkXUnfeasible, nx.maximal_independent_set, G, ["Smith"])
pytest.raises(
nx.NetworkXUnfeasible, nx.maximal_independent_set, G, ["Salviati", "Pazzi"]
)
def test_digraph_exception(self):
G = nx.DiGraph([(1, 2), (3, 4)])
pytest.raises(nx.NetworkXNotImplemented, nx.maximal_independent_set, G)
def test_florentine_family(self):
G = self.florentine
indep = nx.maximal_independent_set(G, ["Medici", "Bischeri"])
assert sorted(indep) == sorted(
["Medici", "Bischeri", "Castellani", "Pazzi", "Ginori", "Lamberteschi"]
)
def test_bipartite(self):
G = nx.complete_bipartite_graph(12, 34)
indep = nx.maximal_independent_set(G, [4, 5, 9, 10])
assert sorted(indep) == list(range(12))
def test_random_graphs(self):
"""Generate 50 random graphs of different types and sizes and
make sure that all sets are independent and maximal."""
for i in range(0, 50, 10):
G = nx.random_graphs.erdos_renyi_graph(i * 10 + 1, random.random())
IS = nx.maximal_independent_set(G)
assert not list(G.subgraph(IS).edges())
neighbors_of_MIS = set.union(*(set(G.neighbors(v)) for v in IS))
for v in set(G.nodes()).difference(IS):
assert v in neighbors_of_MIS

View file

@ -0,0 +1,16 @@
import networkx as nx
from networkx.algorithms.moral import moral_graph
def test_get_moral_graph():
graph = nx.DiGraph()
graph.add_nodes_from([1, 2, 3, 4, 5, 6, 7])
graph.add_edges_from([(1, 2), (3, 2), (4, 1), (4, 5), (6, 5), (7, 5)])
H = moral_graph(graph)
assert not H.is_directed()
assert H.has_edge(1, 3)
assert H.has_edge(4, 6)
assert H.has_edge(6, 7)
assert H.has_edge(4, 7)
assert not H.has_edge(1, 5)

View file

@ -0,0 +1,14 @@
import networkx as nx
import pytest
numpy = pytest.importorskip("numpy")
npt = pytest.importorskip("numpy.testing")
def test_non_randomness():
G = nx.karate_club_graph()
npt.assert_almost_equal(nx.non_randomness(G, 2)[0], 11.7, decimal=2)
npt.assert_almost_equal(
nx.non_randomness(G)[0], 7.21, decimal=2
) # infers 3 communities

View file

@ -0,0 +1,272 @@
import pytest
import networkx as nx
from networkx.algorithms.planar_drawing import triangulate_embedding
import math
def test_graph1():
embedding_data = {0: [1, 2, 3], 1: [2, 0], 2: [3, 0, 1], 3: [2, 0]}
check_embedding_data(embedding_data)
def test_graph2():
embedding_data = {
0: [8, 6],
1: [2, 6, 9],
2: [8, 1, 7, 9, 6, 4],
3: [9],
4: [2],
5: [6, 8],
6: [9, 1, 0, 5, 2],
7: [9, 2],
8: [0, 2, 5],
9: [1, 6, 2, 7, 3],
}
check_embedding_data(embedding_data)
def test_circle_graph():
embedding_data = {
0: [1, 9],
1: [0, 2],
2: [1, 3],
3: [2, 4],
4: [3, 5],
5: [4, 6],
6: [5, 7],
7: [6, 8],
8: [7, 9],
9: [8, 0],
}
check_embedding_data(embedding_data)
def test_grid_graph():
embedding_data = {
(0, 1): [(0, 0), (1, 1), (0, 2)],
(1, 2): [(1, 1), (2, 2), (0, 2)],
(0, 0): [(0, 1), (1, 0)],
(2, 1): [(2, 0), (2, 2), (1, 1)],
(1, 1): [(2, 1), (1, 2), (0, 1), (1, 0)],
(2, 0): [(1, 0), (2, 1)],
(2, 2): [(1, 2), (2, 1)],
(1, 0): [(0, 0), (2, 0), (1, 1)],
(0, 2): [(1, 2), (0, 1)],
}
check_embedding_data(embedding_data)
def test_one_node_graph():
embedding_data = {0: []}
check_embedding_data(embedding_data)
def test_two_node_graph():
embedding_data = {0: [1], 1: [0]}
check_embedding_data(embedding_data)
def test_three_node_graph():
embedding_data = {0: [1, 2], 1: [0, 2], 2: [0, 1]}
check_embedding_data(embedding_data)
def test_multiple_component_graph1():
embedding_data = {0: [], 1: []}
check_embedding_data(embedding_data)
def test_multiple_component_graph2():
embedding_data = {0: [1, 2], 1: [0, 2], 2: [0, 1], 3: [4, 5], 4: [3, 5], 5: [3, 4]}
check_embedding_data(embedding_data)
def test_invalid_half_edge():
with pytest.raises(nx.NetworkXException):
embedding_data = {1: [2, 3, 4], 2: [1, 3, 4], 3: [1, 2, 4], 4: [1, 2, 3]}
embedding = nx.PlanarEmbedding()
embedding.set_data(embedding_data)
nx.combinatorial_embedding_to_pos(embedding)
def test_triangulate_embedding1():
embedding = nx.PlanarEmbedding()
embedding.add_node(1)
expected_embedding = {1: []}
check_triangulation(embedding, expected_embedding)
def test_triangulate_embedding2():
embedding = nx.PlanarEmbedding()
embedding.connect_components(1, 2)
expected_embedding = {1: [2], 2: [1]}
check_triangulation(embedding, expected_embedding)
def check_triangulation(embedding, expected_embedding):
res_embedding, _ = triangulate_embedding(embedding, True)
assert (
res_embedding.get_data() == expected_embedding
), "Expected embedding incorrect"
res_embedding, _ = triangulate_embedding(embedding, False)
assert (
res_embedding.get_data() == expected_embedding
), "Expected embedding incorrect"
def check_embedding_data(embedding_data):
"""Checks that the planar embedding of the input is correct"""
embedding = nx.PlanarEmbedding()
embedding.set_data(embedding_data)
pos_fully = nx.combinatorial_embedding_to_pos(embedding, False)
msg = "Planar drawing does not conform to the embedding (fully " "triangulation)"
assert planar_drawing_conforms_to_embedding(embedding, pos_fully), msg
check_edge_intersections(embedding, pos_fully)
pos_internally = nx.combinatorial_embedding_to_pos(embedding, True)
msg = "Planar drawing does not conform to the embedding (internal " "triangulation)"
assert planar_drawing_conforms_to_embedding(embedding, pos_internally), msg
check_edge_intersections(embedding, pos_internally)
def is_close(a, b, rel_tol=1e-09, abs_tol=0.0):
# Check if float numbers are basically equal, for python >=3.5 there is
# function for that in the standard library
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
def point_in_between(a, b, p):
# checks if p is on the line between a and b
x1, y1 = a
x2, y2 = b
px, py = p
dist_1_2 = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
dist_1_p = math.sqrt((x1 - px) ** 2 + (y1 - py) ** 2)
dist_2_p = math.sqrt((x2 - px) ** 2 + (y2 - py) ** 2)
return is_close(dist_1_p + dist_2_p, dist_1_2)
def check_edge_intersections(G, pos):
"""Check all edges in G for intersections.
Raises an exception if an intersection is found.
Parameters
----------
G : NetworkX graph
pos : dict
Maps every node to a tuple (x, y) representing its position
"""
for a, b in G.edges():
for c, d in G.edges():
# Check if end points are different
if a != c and b != d and b != c and a != d:
x1, y1 = pos[a]
x2, y2 = pos[b]
x3, y3 = pos[c]
x4, y4 = pos[d]
determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if determinant != 0: # the lines are not parallel
# calculate intersection point, see:
# https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
px = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (
x3 * y4 - y3 * x4
) / float(determinant)
py = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (
x3 * y4 - y3 * x4
) / float(determinant)
# Check if intersection lies between the points
if point_in_between(pos[a], pos[b], (px, py)) and point_in_between(
pos[c], pos[d], (px, py)
):
msg = f"There is an intersection at {px},{py}"
raise nx.NetworkXException(msg)
# Check overlap
msg = "A node lies on a edge connecting two other nodes"
if (
point_in_between(pos[a], pos[b], pos[c])
or point_in_between(pos[a], pos[b], pos[d])
or point_in_between(pos[c], pos[d], pos[a])
or point_in_between(pos[c], pos[d], pos[b])
):
raise nx.NetworkXException(msg)
# No edge intersection found
class Vector:
"""Compare vectors by their angle without loss of precision
All vectors in direction [0, 1] are the smallest.
The vectors grow in clockwise direction.
"""
__slots__ = ["x", "y", "node", "quadrant"]
def __init__(self, x, y, node):
self.x = x
self.y = y
self.node = node
if self.x >= 0 and self.y > 0:
self.quadrant = 1
elif self.x > 0 and self.y <= 0:
self.quadrant = 2
elif self.x <= 0 and self.y < 0:
self.quadrant = 3
else:
self.quadrant = 4
def __eq__(self, other):
return self.quadrant == other.quadrant and self.x * other.y == self.y * other.x
def __lt__(self, other):
if self.quadrant < other.quadrant:
return True
elif self.quadrant > other.quadrant:
return False
else:
return self.x * other.y < self.y * other.x
def __ne__(self, other):
return not self == other
def __le__(self, other):
return not other < self
def __gt__(self, other):
return other < self
def __ge__(self, other):
return not self < other
def planar_drawing_conforms_to_embedding(embedding, pos):
"""Checks if pos conforms to the planar embedding
Returns true iff the neighbors are actually oriented in the orientation
specified of the embedding
"""
for v in embedding:
nbr_vectors = []
v_pos = pos[v]
for nbr in embedding[v]:
new_vector = Vector(pos[nbr][0] - v_pos[0], pos[nbr][1] - v_pos[1], nbr)
nbr_vectors.append(new_vector)
# Sort neighbors according to their phi angle
nbr_vectors.sort()
for idx, nbr_vector in enumerate(nbr_vectors):
cw_vector = nbr_vectors[(idx + 1) % len(nbr_vectors)]
ccw_vector = nbr_vectors[idx - 1]
if (
embedding[v][nbr_vector.node]["cw"] != cw_vector.node
or embedding[v][nbr_vector.node]["ccw"] != ccw_vector.node
):
return False
if cw_vector.node != nbr_vector.node and cw_vector == nbr_vector:
# Lines overlap
return False
if ccw_vector.node != nbr_vector.node and ccw_vector == nbr_vector:
# Lines overlap
return False
return True

View file

@ -0,0 +1,439 @@
import pytest
import networkx as nx
from networkx.algorithms.planarity import get_counterexample
from networkx.algorithms.planarity import get_counterexample_recursive
from networkx.algorithms.planarity import check_planarity_recursive
class TestLRPlanarity:
"""Nose Unit tests for the :mod:`networkx.algorithms.planarity` module.
Tests three things:
1. Check that the result is correct
(returns planar if and only if the graph is actually planar)
2. In case a counter example is returned: Check if it is correct
3. In case an embedding is returned: Check if its actually an embedding
"""
@staticmethod
def check_graph(G, is_planar=None):
"""Raises an exception if the lr_planarity check returns a wrong result
Parameters
----------
G : NetworkX graph
is_planar : bool
The expected result of the planarity check.
If set to None only counter example or embedding are verified.
"""
# obtain results of planarity check
is_planar_lr, result = nx.check_planarity(G, True)
is_planar_lr_rec, result_rec = check_planarity_recursive(G, True)
if is_planar is not None:
# set a message for the assert
if is_planar:
msg = "Wrong planarity check result. Should be planar."
else:
msg = "Wrong planarity check result. Should be non-planar."
# check if the result is as expected
assert is_planar == is_planar_lr, msg
assert is_planar == is_planar_lr_rec, msg
if is_planar_lr:
# check embedding
check_embedding(G, result)
check_embedding(G, result_rec)
else:
# check counter example
check_counterexample(G, result)
check_counterexample(G, result_rec)
def test_simple_planar_graph(self):
e = [
(1, 2),
(2, 3),
(3, 4),
(4, 6),
(6, 7),
(7, 1),
(1, 5),
(5, 2),
(2, 4),
(4, 5),
(5, 7),
]
self.check_graph(nx.Graph(e), is_planar=True)
def test_planar_with_selfloop(self):
e = [
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(1, 2),
(1, 3),
(1, 5),
(2, 5),
(2, 4),
(3, 4),
(3, 5),
(4, 5),
]
self.check_graph(nx.Graph(e), is_planar=True)
def test_k3_3(self):
self.check_graph(nx.complete_bipartite_graph(3, 3), is_planar=False)
def test_k5(self):
self.check_graph(nx.complete_graph(5), is_planar=False)
def test_multiple_components_planar(self):
e = [(1, 2), (2, 3), (3, 1), (4, 5), (5, 6), (6, 4)]
self.check_graph(nx.Graph(e), is_planar=True)
def test_multiple_components_non_planar(self):
G = nx.complete_graph(5)
# add another planar component to the non planar component
# G stays non planar
G.add_edges_from([(6, 7), (7, 8), (8, 6)])
self.check_graph(G, is_planar=False)
def test_non_planar_with_selfloop(self):
G = nx.complete_graph(5)
# add self loops
for i in range(5):
G.add_edge(i, i)
self.check_graph(G, is_planar=False)
def test_non_planar1(self):
# tests a graph that has no subgraph directly isomorph to K5 or K3_3
e = [
(1, 5),
(1, 6),
(1, 7),
(2, 6),
(2, 3),
(3, 5),
(3, 7),
(4, 5),
(4, 6),
(4, 7),
]
self.check_graph(nx.Graph(e), is_planar=False)
def test_loop(self):
# test a graph with a selfloop
e = [(1, 2), (2, 2)]
G = nx.Graph(e)
self.check_graph(G, is_planar=True)
def test_comp(self):
# test multiple component graph
e = [(1, 2), (3, 4)]
G = nx.Graph(e)
G.remove_edge(1, 2)
self.check_graph(G, is_planar=True)
def test_goldner_harary(self):
# test goldner-harary graph (a maximal planar graph)
e = [
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 7),
(1, 8),
(1, 10),
(1, 11),
(2, 3),
(2, 4),
(2, 6),
(2, 7),
(2, 9),
(2, 10),
(2, 11),
(3, 4),
(4, 5),
(4, 6),
(4, 7),
(5, 7),
(6, 7),
(7, 8),
(7, 9),
(7, 10),
(8, 10),
(9, 10),
(10, 11),
]
G = nx.Graph(e)
self.check_graph(G, is_planar=True)
def test_planar_multigraph(self):
G = nx.MultiGraph([(1, 2), (1, 2), (1, 2), (1, 2), (2, 3), (3, 1)])
self.check_graph(G, is_planar=True)
def test_non_planar_multigraph(self):
G = nx.MultiGraph(nx.complete_graph(5))
G.add_edges_from([(1, 2)] * 5)
self.check_graph(G, is_planar=False)
def test_planar_digraph(self):
G = nx.DiGraph([(1, 2), (2, 3), (2, 4), (4, 1), (4, 2), (1, 4), (3, 2)])
self.check_graph(G, is_planar=True)
def test_non_planar_digraph(self):
G = nx.DiGraph(nx.complete_graph(5))
G.remove_edge(1, 2)
G.remove_edge(4, 1)
self.check_graph(G, is_planar=False)
def test_single_component(self):
# Test a graph with only a single node
G = nx.Graph()
G.add_node(1)
self.check_graph(G, is_planar=True)
def test_graph1(self):
G = nx.OrderedGraph(
[
(3, 10),
(2, 13),
(1, 13),
(7, 11),
(0, 8),
(8, 13),
(0, 2),
(0, 7),
(0, 10),
(1, 7),
]
)
self.check_graph(G, is_planar=True)
def test_graph2(self):
G = nx.OrderedGraph(
[
(1, 2),
(4, 13),
(0, 13),
(4, 5),
(7, 10),
(1, 7),
(0, 3),
(2, 6),
(5, 6),
(7, 13),
(4, 8),
(0, 8),
(0, 9),
(2, 13),
(6, 7),
(3, 6),
(2, 8),
]
)
self.check_graph(G, is_planar=False)
def test_graph3(self):
G = nx.OrderedGraph(
[
(0, 7),
(3, 11),
(3, 4),
(8, 9),
(4, 11),
(1, 7),
(1, 13),
(1, 11),
(3, 5),
(5, 7),
(1, 3),
(0, 4),
(5, 11),
(5, 13),
]
)
self.check_graph(G, is_planar=False)
def test_counterexample_planar(self):
with pytest.raises(nx.NetworkXException):
# Try to get a counterexample of a planar graph
G = nx.Graph()
G.add_node(1)
get_counterexample(G)
def test_counterexample_planar_recursive(self):
with pytest.raises(nx.NetworkXException):
# Try to get a counterexample of a planar graph
G = nx.Graph()
G.add_node(1)
get_counterexample_recursive(G)
def check_embedding(G, embedding):
"""Raises an exception if the combinatorial embedding is not correct
Parameters
----------
G : NetworkX graph
embedding : a dict mapping nodes to a list of edges
This specifies the ordering of the outgoing edges from a node for
a combinatorial embedding
Notes
-----
Checks the following things:
- The type of the embedding is correct
- The nodes and edges match the original graph
- Every half edge has its matching opposite half edge
- No intersections of edges (checked by Euler's formula)
"""
if not isinstance(embedding, nx.PlanarEmbedding):
raise nx.NetworkXException("Bad embedding. Not of type nx.PlanarEmbedding")
# Check structure
embedding.check_structure()
# Check that graphs are equivalent
assert set(G.nodes) == set(
embedding.nodes
), "Bad embedding. Nodes don't match the original graph."
# Check that the edges are equal
g_edges = set()
for edge in G.edges:
if edge[0] != edge[1]:
g_edges.add((edge[0], edge[1]))
g_edges.add((edge[1], edge[0]))
assert g_edges == set(
embedding.edges
), "Bad embedding. Edges don't match the original graph."
def check_counterexample(G, sub_graph):
"""Raises an exception if the counterexample is wrong.
Parameters
----------
G : NetworkX graph
subdivision_nodes : set
A set of nodes inducing a subgraph as a counterexample
"""
# 1. Create the sub graph
sub_graph = nx.Graph(sub_graph)
# 2. Remove self loops
for u in sub_graph:
if sub_graph.has_edge(u, u):
sub_graph.remove_edge(u, u)
# keep track of nodes we might need to contract
contract = list(sub_graph)
# 3. Contract Edges
while len(contract) > 0:
contract_node = contract.pop()
if contract_node not in sub_graph:
# Node was already contracted
continue
degree = sub_graph.degree[contract_node]
# Check if we can remove the node
if degree == 2:
# Get the two neighbors
neighbors = iter(sub_graph[contract_node])
u = next(neighbors)
v = next(neighbors)
# Save nodes for later
contract.append(u)
contract.append(v)
# Contract edge
sub_graph.remove_node(contract_node)
sub_graph.add_edge(u, v)
# 4. Check for isomorphism with K5 or K3_3 graphs
if len(sub_graph) == 5:
if not nx.is_isomorphic(nx.complete_graph(5), sub_graph):
raise nx.NetworkXException("Bad counter example.")
elif len(sub_graph) == 6:
if not nx.is_isomorphic(nx.complete_bipartite_graph(3, 3), sub_graph):
raise nx.NetworkXException("Bad counter example.")
else:
raise nx.NetworkXException("Bad counter example.")
class TestPlanarEmbeddingClass:
def test_get_data(self):
embedding = self.get_star_embedding(3)
data = embedding.get_data()
data_cmp = {0: [2, 1], 1: [0], 2: [0]}
assert data == data_cmp
def test_missing_edge_orientation(self):
with pytest.raises(nx.NetworkXException):
embedding = nx.PlanarEmbedding()
embedding.add_edge(1, 2)
embedding.add_edge(2, 1)
# Invalid structure because the orientation of the edge was not set
embedding.check_structure()
def test_invalid_edge_orientation(self):
with pytest.raises(nx.NetworkXException):
embedding = nx.PlanarEmbedding()
embedding.add_half_edge_first(1, 2)
embedding.add_half_edge_first(2, 1)
embedding.add_edge(1, 3)
embedding.check_structure()
def test_missing_half_edge(self):
with pytest.raises(nx.NetworkXException):
embedding = nx.PlanarEmbedding()
embedding.add_half_edge_first(1, 2)
# Invalid structure because other half edge is missing
embedding.check_structure()
def test_not_fulfilling_euler_formula(self):
with pytest.raises(nx.NetworkXException):
embedding = nx.PlanarEmbedding()
for i in range(5):
for j in range(5):
if i != j:
embedding.add_half_edge_first(i, j)
embedding.check_structure()
def test_missing_reference(self):
with pytest.raises(nx.NetworkXException):
embedding = nx.PlanarEmbedding()
embedding.add_half_edge_cw(1, 2, 3)
def test_connect_components(self):
embedding = nx.PlanarEmbedding()
embedding.connect_components(1, 2)
def test_successful_face_traversal(self):
embedding = nx.PlanarEmbedding()
embedding.add_half_edge_first(1, 2)
embedding.add_half_edge_first(2, 1)
face = embedding.traverse_face(1, 2)
assert face == [1, 2]
def test_unsuccessful_face_traversal(self):
with pytest.raises(nx.NetworkXException):
embedding = nx.PlanarEmbedding()
embedding.add_edge(1, 2, ccw=2, cw=3)
embedding.add_edge(2, 1, ccw=1, cw=3)
embedding.traverse_face(1, 2)
@staticmethod
def get_star_embedding(n):
embedding = nx.PlanarEmbedding()
for i in range(1, n):
embedding.add_half_edge_first(0, i)
embedding.add_half_edge_first(i, 0)
return embedding

View file

@ -0,0 +1,37 @@
import pytest
import networkx as nx
class TestReciprocity:
# test overall reicprocity by passing whole graph
def test_reciprocity_digraph(self):
DG = nx.DiGraph([(1, 2), (2, 1)])
reciprocity = nx.reciprocity(DG)
assert reciprocity == 1.0
# test empty graph's overall reciprocity which will throw an error
def test_overall_reciprocity_empty_graph(self):
with pytest.raises(nx.NetworkXError):
DG = nx.DiGraph()
nx.overall_reciprocity(DG)
# test for reciprocity for a list of nodes
def test_reciprocity_graph_nodes(self):
DG = nx.DiGraph([(1, 2), (2, 3), (3, 2)])
reciprocity = nx.reciprocity(DG, [1, 2])
expected_reciprocity = {1: 0.0, 2: 0.6666666666666666}
assert reciprocity == expected_reciprocity
# test for reciprocity for a single node
def test_reciprocity_graph_node(self):
DG = nx.DiGraph([(1, 2), (2, 3), (3, 2)])
reciprocity = nx.reciprocity(DG, 2)
assert reciprocity == 0.6666666666666666
# test for reciprocity for an isolated node
def test_reciprocity_graph_isolated_nodes(self):
with pytest.raises(nx.NetworkXError):
DG = nx.DiGraph([(1, 2)])
DG.add_node(4)
nx.reciprocity(DG, 4)

View file

@ -0,0 +1,81 @@
import pytest
import networkx
import networkx as nx
import networkx.algorithms.regular as reg
import networkx.generators as gen
class TestKFactor:
def test_k_factor_trivial(self):
g = gen.cycle_graph(4)
f = reg.k_factor(g, 2)
assert g.edges == f.edges
def test_k_factor1(self):
g = gen.grid_2d_graph(4, 4)
g_kf = reg.k_factor(g, 2)
for edge in g_kf.edges():
assert g.has_edge(edge[0], edge[1])
for _, degree in g_kf.degree():
assert degree == 2
def test_k_factor2(self):
g = gen.complete_graph(6)
g_kf = reg.k_factor(g, 3)
for edge in g_kf.edges():
assert g.has_edge(edge[0], edge[1])
for _, degree in g_kf.degree():
assert degree == 3
def test_k_factor3(self):
g = gen.grid_2d_graph(4, 4)
with pytest.raises(nx.NetworkXUnfeasible):
reg.k_factor(g, 3)
def test_k_factor4(self):
g = gen.lattice.hexagonal_lattice_graph(4, 4)
# Perfect matching doesn't exist for 4,4 hexagonal lattice graph
with pytest.raises(nx.NetworkXUnfeasible):
reg.k_factor(g, 2)
def test_k_factor5(self):
g = gen.complete_graph(6)
# small k to exercise SmallKGadget
g_kf = reg.k_factor(g, 2)
for edge in g_kf.edges():
assert g.has_edge(edge[0], edge[1])
for _, degree in g_kf.degree():
assert degree == 2
class TestIsRegular:
def test_is_regular1(self):
g = gen.cycle_graph(4)
assert reg.is_regular(g)
def test_is_regular2(self):
g = gen.complete_graph(5)
assert reg.is_regular(g)
def test_is_regular3(self):
g = gen.lollipop_graph(5, 5)
assert not reg.is_regular(g)
class TestIsKRegular:
def test_is_k_regular1(self):
g = gen.cycle_graph(4)
assert reg.is_k_regular(g, 2)
assert not reg.is_k_regular(g, 3)
def test_is_k_regular2(self):
g = gen.complete_graph(5)
assert reg.is_k_regular(g, 4)
assert not reg.is_k_regular(g, 3)
assert not reg.is_k_regular(g, 6)
def test_is_k_regular3(self):
g = gen.lollipop_graph(5, 5)
assert not reg.is_k_regular(g, 5)
assert not reg.is_k_regular(g, 6)

View file

@ -0,0 +1,85 @@
import pytest
import networkx as nx
def test_richclub():
G = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (1, 4), (4, 5)])
rc = nx.richclub.rich_club_coefficient(G, normalized=False)
assert rc == {0: 12.0 / 30, 1: 8.0 / 12}
# test single value
rc0 = nx.richclub.rich_club_coefficient(G, normalized=False)[0]
assert rc0 == 12.0 / 30.0
def test_richclub_seed():
G = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (1, 4), (4, 5)])
rcNorm = nx.richclub.rich_club_coefficient(G, Q=2, seed=1)
assert rcNorm == {0: 1.0, 1: 1.0}
def test_richclub_normalized():
G = nx.Graph([(0, 1), (0, 2), (1, 2), (1, 3), (1, 4), (4, 5)])
rcNorm = nx.richclub.rich_club_coefficient(G, Q=2)
assert rcNorm == {0: 1.0, 1: 1.0}
def test_richclub2():
T = nx.balanced_tree(2, 10)
rc = nx.richclub.rich_club_coefficient(T, normalized=False)
assert rc == {
0: 4092 / (2047 * 2046.0),
1: (2044.0 / (1023 * 1022)),
2: (2040.0 / (1022 * 1021)),
}
def test_richclub3():
# tests edgecase
G = nx.karate_club_graph()
rc = nx.rich_club_coefficient(G, normalized=False)
assert rc == {
0: 156.0 / 1122,
1: 154.0 / 1056,
2: 110.0 / 462,
3: 78.0 / 240,
4: 44.0 / 90,
5: 22.0 / 42,
6: 10.0 / 20,
7: 10.0 / 20,
8: 10.0 / 20,
9: 6.0 / 12,
10: 2.0 / 6,
11: 2.0 / 6,
12: 0.0,
13: 0.0,
14: 0.0,
15: 0.0,
}
def test_richclub4():
G = nx.Graph()
G.add_edges_from(
[(0, 1), (0, 2), (0, 3), (0, 4), (4, 5), (5, 9), (6, 9), (7, 9), (8, 9)]
)
rc = nx.rich_club_coefficient(G, normalized=False)
assert rc == {0: 18 / 90.0, 1: 6 / 12.0, 2: 0.0, 3: 0.0}
def test_richclub_exception():
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.DiGraph()
nx.rich_club_coefficient(G)
def test_rich_club_exception2():
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.MultiGraph()
nx.rich_club_coefficient(G)
# def test_richclub2_normalized():
# T = nx.balanced_tree(2,10)
# rcNorm = nx.richclub.rich_club_coefficient(T,Q=2)
# assert_true(rcNorm[0] ==1.0 and rcNorm[1] < 0.9 and rcNorm[2] < 0.9)

View file

@ -0,0 +1,715 @@
import pytest
import networkx as nx
from networkx.algorithms.similarity import (
graph_edit_distance,
optimal_edit_paths,
optimize_graph_edit_distance,
)
from networkx.generators.classic import (
circular_ladder_graph,
cycle_graph,
path_graph,
wheel_graph,
)
def nmatch(n1, n2):
return n1 == n2
def ematch(e1, e2):
return e1 == e2
def getCanonical():
G = nx.Graph()
G.add_node("A", label="A")
G.add_node("B", label="B")
G.add_node("C", label="C")
G.add_node("D", label="D")
G.add_edge("A", "B", label="a-b")
G.add_edge("B", "C", label="b-c")
G.add_edge("B", "D", label="b-d")
return G
class TestSimilarity:
@classmethod
def setup_class(cls):
global numpy
global scipy
numpy = pytest.importorskip("numpy")
scipy = pytest.importorskip("scipy")
def test_graph_edit_distance_roots_and_timeout(self):
G0 = nx.star_graph(5)
G1 = G0.copy()
pytest.raises(ValueError, graph_edit_distance, G0, G1, roots=[2])
pytest.raises(ValueError, graph_edit_distance, G0, G1, roots=[2, 3, 4])
pytest.raises(nx.NodeNotFound, graph_edit_distance, G0, G1, roots=(9, 3))
pytest.raises(nx.NodeNotFound, graph_edit_distance, G0, G1, roots=(3, 9))
pytest.raises(nx.NodeNotFound, graph_edit_distance, G0, G1, roots=(9, 9))
assert graph_edit_distance(G0, G1, roots=(1, 2)) == 0
assert graph_edit_distance(G0, G1, roots=(0, 1)) == 8
assert graph_edit_distance(G0, G1, roots=(1, 2), timeout=5) == 0
assert graph_edit_distance(G0, G1, roots=(0, 1), timeout=5) == 8
assert graph_edit_distance(G0, G1, roots=(0, 1), timeout=0.0001) is None
# test raise on 0 timeout
pytest.raises(nx.NetworkXError, graph_edit_distance, G0, G1, timeout=0)
def test_graph_edit_distance(self):
G0 = nx.Graph()
G1 = path_graph(6)
G2 = cycle_graph(6)
G3 = wheel_graph(7)
assert graph_edit_distance(G0, G0) == 0
assert graph_edit_distance(G0, G1) == 11
assert graph_edit_distance(G1, G0) == 11
assert graph_edit_distance(G0, G2) == 12
assert graph_edit_distance(G2, G0) == 12
assert graph_edit_distance(G0, G3) == 19
assert graph_edit_distance(G3, G0) == 19
assert graph_edit_distance(G1, G1) == 0
assert graph_edit_distance(G1, G2) == 1
assert graph_edit_distance(G2, G1) == 1
assert graph_edit_distance(G1, G3) == 8
assert graph_edit_distance(G3, G1) == 8
assert graph_edit_distance(G2, G2) == 0
assert graph_edit_distance(G2, G3) == 7
assert graph_edit_distance(G3, G2) == 7
assert graph_edit_distance(G3, G3) == 0
def test_graph_edit_distance_node_match(self):
G1 = cycle_graph(5)
G2 = cycle_graph(5)
for n, attr in G1.nodes.items():
attr["color"] = "red" if n % 2 == 0 else "blue"
for n, attr in G2.nodes.items():
attr["color"] = "red" if n % 2 == 1 else "blue"
assert graph_edit_distance(G1, G2) == 0
assert (
graph_edit_distance(
G1, G2, node_match=lambda n1, n2: n1["color"] == n2["color"]
)
== 1
)
def test_graph_edit_distance_edge_match(self):
G1 = path_graph(6)
G2 = path_graph(6)
for e, attr in G1.edges.items():
attr["color"] = "red" if min(e) % 2 == 0 else "blue"
for e, attr in G2.edges.items():
attr["color"] = "red" if min(e) // 3 == 0 else "blue"
assert graph_edit_distance(G1, G2) == 0
assert (
graph_edit_distance(
G1, G2, edge_match=lambda e1, e2: e1["color"] == e2["color"]
)
== 2
)
def test_graph_edit_distance_node_cost(self):
G1 = path_graph(6)
G2 = path_graph(6)
for n, attr in G1.nodes.items():
attr["color"] = "red" if n % 2 == 0 else "blue"
for n, attr in G2.nodes.items():
attr["color"] = "red" if n % 2 == 1 else "blue"
def node_subst_cost(uattr, vattr):
if uattr["color"] == vattr["color"]:
return 1
else:
return 10
def node_del_cost(attr):
if attr["color"] == "blue":
return 20
else:
return 50
def node_ins_cost(attr):
if attr["color"] == "blue":
return 40
else:
return 100
assert (
graph_edit_distance(
G1,
G2,
node_subst_cost=node_subst_cost,
node_del_cost=node_del_cost,
node_ins_cost=node_ins_cost,
)
== 6
)
def test_graph_edit_distance_edge_cost(self):
G1 = path_graph(6)
G2 = path_graph(6)
for e, attr in G1.edges.items():
attr["color"] = "red" if min(e) % 2 == 0 else "blue"
for e, attr in G2.edges.items():
attr["color"] = "red" if min(e) // 3 == 0 else "blue"
def edge_subst_cost(gattr, hattr):
if gattr["color"] == hattr["color"]:
return 0.01
else:
return 0.1
def edge_del_cost(attr):
if attr["color"] == "blue":
return 0.2
else:
return 0.5
def edge_ins_cost(attr):
if attr["color"] == "blue":
return 0.4
else:
return 1.0
assert (
graph_edit_distance(
G1,
G2,
edge_subst_cost=edge_subst_cost,
edge_del_cost=edge_del_cost,
edge_ins_cost=edge_ins_cost,
)
== 0.23
)
def test_graph_edit_distance_upper_bound(self):
G1 = circular_ladder_graph(2)
G2 = circular_ladder_graph(6)
assert graph_edit_distance(G1, G2, upper_bound=5) is None
assert graph_edit_distance(G1, G2, upper_bound=24) == 22
assert graph_edit_distance(G1, G2) == 22
def test_optimal_edit_paths(self):
G1 = path_graph(3)
G2 = cycle_graph(3)
paths, cost = optimal_edit_paths(G1, G2)
assert cost == 1
assert len(paths) == 6
def canonical(vertex_path, edge_path):
return (
tuple(sorted(vertex_path)),
tuple(sorted(edge_path, key=lambda x: (None in x, x))),
)
expected_paths = [
(
[(0, 0), (1, 1), (2, 2)],
[((0, 1), (0, 1)), ((1, 2), (1, 2)), (None, (0, 2))],
),
(
[(0, 0), (1, 2), (2, 1)],
[((0, 1), (0, 2)), ((1, 2), (1, 2)), (None, (0, 1))],
),
(
[(0, 1), (1, 0), (2, 2)],
[((0, 1), (0, 1)), ((1, 2), (0, 2)), (None, (1, 2))],
),
(
[(0, 1), (1, 2), (2, 0)],
[((0, 1), (1, 2)), ((1, 2), (0, 2)), (None, (0, 1))],
),
(
[(0, 2), (1, 0), (2, 1)],
[((0, 1), (0, 2)), ((1, 2), (0, 1)), (None, (1, 2))],
),
(
[(0, 2), (1, 1), (2, 0)],
[((0, 1), (1, 2)), ((1, 2), (0, 1)), (None, (0, 2))],
),
]
assert {canonical(*p) for p in paths} == {canonical(*p) for p in expected_paths}
def test_optimize_graph_edit_distance(self):
G1 = circular_ladder_graph(2)
G2 = circular_ladder_graph(6)
bestcost = 1000
for cost in optimize_graph_edit_distance(G1, G2):
assert cost < bestcost
bestcost = cost
assert bestcost == 22
# def test_graph_edit_distance_bigger(self):
# G1 = circular_ladder_graph(12)
# G2 = circular_ladder_graph(16)
# assert_equal(graph_edit_distance(G1, G2), 22)
def test_selfloops(self):
G0 = nx.Graph()
G1 = nx.Graph()
G1.add_edges_from((("A", "A"), ("A", "B")))
G2 = nx.Graph()
G2.add_edges_from((("A", "B"), ("B", "B")))
G3 = nx.Graph()
G3.add_edges_from((("A", "A"), ("A", "B"), ("B", "B")))
assert graph_edit_distance(G0, G0) == 0
assert graph_edit_distance(G0, G1) == 4
assert graph_edit_distance(G1, G0) == 4
assert graph_edit_distance(G0, G2) == 4
assert graph_edit_distance(G2, G0) == 4
assert graph_edit_distance(G0, G3) == 5
assert graph_edit_distance(G3, G0) == 5
assert graph_edit_distance(G1, G1) == 0
assert graph_edit_distance(G1, G2) == 0
assert graph_edit_distance(G2, G1) == 0
assert graph_edit_distance(G1, G3) == 1
assert graph_edit_distance(G3, G1) == 1
assert graph_edit_distance(G2, G2) == 0
assert graph_edit_distance(G2, G3) == 1
assert graph_edit_distance(G3, G2) == 1
assert graph_edit_distance(G3, G3) == 0
def test_digraph(self):
G0 = nx.DiGraph()
G1 = nx.DiGraph()
G1.add_edges_from((("A", "B"), ("B", "C"), ("C", "D"), ("D", "A")))
G2 = nx.DiGraph()
G2.add_edges_from((("A", "B"), ("B", "C"), ("C", "D"), ("A", "D")))
G3 = nx.DiGraph()
G3.add_edges_from((("A", "B"), ("A", "C"), ("B", "D"), ("C", "D")))
assert graph_edit_distance(G0, G0) == 0
assert graph_edit_distance(G0, G1) == 8
assert graph_edit_distance(G1, G0) == 8
assert graph_edit_distance(G0, G2) == 8
assert graph_edit_distance(G2, G0) == 8
assert graph_edit_distance(G0, G3) == 8
assert graph_edit_distance(G3, G0) == 8
assert graph_edit_distance(G1, G1) == 0
assert graph_edit_distance(G1, G2) == 2
assert graph_edit_distance(G2, G1) == 2
assert graph_edit_distance(G1, G3) == 4
assert graph_edit_distance(G3, G1) == 4
assert graph_edit_distance(G2, G2) == 0
assert graph_edit_distance(G2, G3) == 2
assert graph_edit_distance(G3, G2) == 2
assert graph_edit_distance(G3, G3) == 0
def test_multigraph(self):
G0 = nx.MultiGraph()
G1 = nx.MultiGraph()
G1.add_edges_from((("A", "B"), ("B", "C"), ("A", "C")))
G2 = nx.MultiGraph()
G2.add_edges_from((("A", "B"), ("B", "C"), ("B", "C"), ("A", "C")))
G3 = nx.MultiGraph()
G3.add_edges_from((("A", "B"), ("B", "C"), ("A", "C"), ("A", "C"), ("A", "C")))
assert graph_edit_distance(G0, G0) == 0
assert graph_edit_distance(G0, G1) == 6
assert graph_edit_distance(G1, G0) == 6
assert graph_edit_distance(G0, G2) == 7
assert graph_edit_distance(G2, G0) == 7
assert graph_edit_distance(G0, G3) == 8
assert graph_edit_distance(G3, G0) == 8
assert graph_edit_distance(G1, G1) == 0
assert graph_edit_distance(G1, G2) == 1
assert graph_edit_distance(G2, G1) == 1
assert graph_edit_distance(G1, G3) == 2
assert graph_edit_distance(G3, G1) == 2
assert graph_edit_distance(G2, G2) == 0
assert graph_edit_distance(G2, G3) == 1
assert graph_edit_distance(G3, G2) == 1
assert graph_edit_distance(G3, G3) == 0
def test_multidigraph(self):
G1 = nx.MultiDiGraph()
G1.add_edges_from(
(
("hardware", "kernel"),
("kernel", "hardware"),
("kernel", "userspace"),
("userspace", "kernel"),
)
)
G2 = nx.MultiDiGraph()
G2.add_edges_from(
(
("winter", "spring"),
("spring", "summer"),
("summer", "autumn"),
("autumn", "winter"),
)
)
assert graph_edit_distance(G1, G2) == 5
assert graph_edit_distance(G2, G1) == 5
# by https://github.com/jfbeaumont
def testCopy(self):
G = nx.Graph()
G.add_node("A", label="A")
G.add_node("B", label="B")
G.add_edge("A", "B", label="a-b")
assert (
graph_edit_distance(G, G.copy(), node_match=nmatch, edge_match=ematch) == 0
)
def testSame(self):
G1 = nx.Graph()
G1.add_node("A", label="A")
G1.add_node("B", label="B")
G1.add_edge("A", "B", label="a-b")
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_edge("A", "B", label="a-b")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 0
def testOneEdgeLabelDiff(self):
G1 = nx.Graph()
G1.add_node("A", label="A")
G1.add_node("B", label="B")
G1.add_edge("A", "B", label="a-b")
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_edge("A", "B", label="bad")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
def testOneNodeLabelDiff(self):
G1 = nx.Graph()
G1.add_node("A", label="A")
G1.add_node("B", label="B")
G1.add_edge("A", "B", label="a-b")
G2 = nx.Graph()
G2.add_node("A", label="Z")
G2.add_node("B", label="B")
G2.add_edge("A", "B", label="a-b")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
def testOneExtraNode(self):
G1 = nx.Graph()
G1.add_node("A", label="A")
G1.add_node("B", label="B")
G1.add_edge("A", "B", label="a-b")
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_edge("A", "B", label="a-b")
G2.add_node("C", label="C")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
def testOneExtraEdge(self):
G1 = nx.Graph()
G1.add_node("A", label="A")
G1.add_node("B", label="B")
G1.add_node("C", label="C")
G1.add_node("C", label="C")
G1.add_edge("A", "B", label="a-b")
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("A", "C", label="a-c")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
def testOneExtraNodeAndEdge(self):
G1 = nx.Graph()
G1.add_node("A", label="A")
G1.add_node("B", label="B")
G1.add_edge("A", "B", label="a-b")
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("A", "C", label="a-c")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 2
def testGraph1(self):
G1 = getCanonical()
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("D", label="D")
G2.add_node("E", label="E")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("B", "D", label="b-d")
G2.add_edge("D", "E", label="d-e")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 3
def testGraph2(self):
G1 = getCanonical()
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_node("D", label="D")
G2.add_node("E", label="E")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("B", "C", label="b-c")
G2.add_edge("C", "D", label="c-d")
G2.add_edge("C", "E", label="c-e")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 4
def testGraph3(self):
G1 = getCanonical()
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_node("D", label="D")
G2.add_node("E", label="E")
G2.add_node("F", label="F")
G2.add_node("G", label="G")
G2.add_edge("A", "C", label="a-c")
G2.add_edge("A", "D", label="a-d")
G2.add_edge("D", "E", label="d-e")
G2.add_edge("D", "F", label="d-f")
G2.add_edge("D", "G", label="d-g")
G2.add_edge("E", "B", label="e-b")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 12
def testGraph4(self):
G1 = getCanonical()
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_node("D", label="D")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("B", "C", label="b-c")
G2.add_edge("C", "D", label="c-d")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 2
def testGraph4_a(self):
G1 = getCanonical()
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_node("D", label="D")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("B", "C", label="b-c")
G2.add_edge("A", "D", label="a-d")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 2
def testGraph4_b(self):
G1 = getCanonical()
G2 = nx.Graph()
G2.add_node("A", label="A")
G2.add_node("B", label="B")
G2.add_node("C", label="C")
G2.add_node("D", label="D")
G2.add_edge("A", "B", label="a-b")
G2.add_edge("B", "C", label="b-c")
G2.add_edge("B", "D", label="bad")
assert graph_edit_distance(G1, G2, node_match=nmatch, edge_match=ematch) == 1
def test_simrank_no_source_no_target(self):
G = nx.cycle_graph(5)
expected = {
0: {
0: 1,
1: 0.3951219505902448,
2: 0.5707317069281646,
3: 0.5707317069281646,
4: 0.3951219505902449,
},
1: {
0: 0.3951219505902448,
1: 1,
2: 0.3951219505902449,
3: 0.5707317069281646,
4: 0.5707317069281646,
},
2: {
0: 0.5707317069281646,
1: 0.3951219505902449,
2: 1,
3: 0.3951219505902449,
4: 0.5707317069281646,
},
3: {
0: 0.5707317069281646,
1: 0.5707317069281646,
2: 0.3951219505902449,
3: 1,
4: 0.3951219505902449,
},
4: {
0: 0.3951219505902449,
1: 0.5707317069281646,
2: 0.5707317069281646,
3: 0.3951219505902449,
4: 1,
},
}
actual = nx.simrank_similarity(G)
assert expected == actual
# For a DiGraph test, use the first graph from the paper cited in
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
G = nx.DiGraph()
G.add_node(0, label="Univ")
G.add_node(1, label="ProfA")
G.add_node(2, label="ProfB")
G.add_node(3, label="StudentA")
G.add_node(4, label="StudentB")
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
expected = {
0: {0: 1, 1: 0.0, 2: 0.1323363991265798, 3: 0.0, 4: 0.03387811817640443},
1: {0: 0.0, 1: 1, 2: 0.4135512472705618, 3: 0.0, 4: 0.10586911930126384},
2: {
0: 0.1323363991265798,
1: 0.4135512472705618,
2: 1,
3: 0.04234764772050554,
4: 0.08822426608438655,
},
3: {0: 0.0, 1: 0.0, 2: 0.04234764772050554, 3: 1, 4: 0.3308409978164495},
4: {
0: 0.03387811817640443,
1: 0.10586911930126384,
2: 0.08822426608438655,
3: 0.3308409978164495,
4: 1,
},
}
# Use the importance_factor from the paper to get the same numbers.
actual = nx.algorithms.similarity.simrank_similarity(G, importance_factor=0.8)
assert expected == actual
def test_simrank_source_no_target(self):
G = nx.cycle_graph(5)
expected = {
0: 1,
1: 0.3951219505902448,
2: 0.5707317069281646,
3: 0.5707317069281646,
4: 0.3951219505902449,
}
actual = nx.simrank_similarity(G, source=0)
assert expected == actual
# For a DiGraph test, use the first graph from the paper cited in
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
G = nx.DiGraph()
G.add_node(0, label="Univ")
G.add_node(1, label="ProfA")
G.add_node(2, label="ProfB")
G.add_node(3, label="StudentA")
G.add_node(4, label="StudentB")
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
expected = {0: 1, 1: 0.0, 2: 0.1323363991265798, 3: 0.0, 4: 0.03387811817640443}
# Use the importance_factor from the paper to get the same numbers.
actual = nx.algorithms.similarity.simrank_similarity(
G, importance_factor=0.8, source=0
)
assert expected == actual
def test_simrank_source_and_target(self):
G = nx.cycle_graph(5)
expected = 1
actual = nx.simrank_similarity(G, source=0, target=0)
# For a DiGraph test, use the first graph from the paper cited in
# the docs: https://dl.acm.org/doi/pdf/10.1145/775047.775126
G = nx.DiGraph()
G.add_node(0, label="Univ")
G.add_node(1, label="ProfA")
G.add_node(2, label="ProfB")
G.add_node(3, label="StudentA")
G.add_node(4, label="StudentB")
G.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 4), (4, 2), (3, 0)])
expected = 0.1323363991265798
# Use the importance_factor from the paper to get the same numbers.
# Use the pair (0,2) because (0,0) and (0,1) have trivial results.
actual = nx.algorithms.similarity.simrank_similarity(
G, importance_factor=0.8, source=0, target=2
)
assert expected == actual
def test_simrank_numpy_no_source_no_target(self):
G = nx.cycle_graph(5)
expected = numpy.array(
[
[
1.0,
0.3947180735764555,
0.570482097206368,
0.570482097206368,
0.3947180735764555,
],
[
0.3947180735764555,
1.0,
0.3947180735764555,
0.570482097206368,
0.570482097206368,
],
[
0.570482097206368,
0.3947180735764555,
1.0,
0.3947180735764555,
0.570482097206368,
],
[
0.570482097206368,
0.570482097206368,
0.3947180735764555,
1.0,
0.3947180735764555,
],
[
0.3947180735764555,
0.570482097206368,
0.570482097206368,
0.3947180735764555,
1.0,
],
]
)
actual = nx.simrank_similarity_numpy(G)
numpy.testing.assert_allclose(expected, actual, atol=1e-7)
def test_simrank_numpy_source_no_target(self):
G = nx.cycle_graph(5)
expected = numpy.array(
[
1.0,
0.3947180735764555,
0.570482097206368,
0.570482097206368,
0.3947180735764555,
]
)
actual = nx.simrank_similarity_numpy(G, source=0)
numpy.testing.assert_allclose(expected, actual, atol=1e-7)
def test_simrank_numpy_source_and_target(self):
G = nx.cycle_graph(5)
expected = 1.0
actual = nx.simrank_similarity_numpy(G, source=0, target=0)
numpy.testing.assert_allclose(expected, actual, atol=1e-7)

View file

@ -0,0 +1,764 @@
import random
import pytest
import networkx as nx
from networkx import convert_node_labels_to_integers as cnlti
from networkx.algorithms.simple_paths import _bidirectional_dijkstra
from networkx.algorithms.simple_paths import _bidirectional_shortest_path
from networkx.utils import arbitrary_element
from networkx.utils import pairwise
class TestIsSimplePath:
"""Unit tests for the
:func:`networkx.algorithms.simple_paths.is_simple_path` function.
"""
def test_empty_list(self):
"""Tests that the empty list is not a valid path, since there
should be a one-to-one correspondence between paths as lists of
nodes and paths as lists of edges.
"""
G = nx.trivial_graph()
assert not nx.is_simple_path(G, [])
def test_trivial_path(self):
"""Tests that the trivial path, a path of length one, is
considered a simple path in a graph.
"""
G = nx.trivial_graph()
assert nx.is_simple_path(G, [0])
def test_trivial_nonpath(self):
"""Tests that a list whose sole element is an object not in the
graph is not considered a simple path.
"""
G = nx.trivial_graph()
assert not nx.is_simple_path(G, ["not a node"])
def test_simple_path(self):
G = nx.path_graph(2)
assert nx.is_simple_path(G, [0, 1])
def test_non_simple_path(self):
G = nx.path_graph(2)
assert not nx.is_simple_path(G, [0, 1, 0])
def test_cycle(self):
G = nx.cycle_graph(3)
assert not nx.is_simple_path(G, [0, 1, 2, 0])
def test_missing_node(self):
G = nx.path_graph(2)
assert not nx.is_simple_path(G, [0, 2])
def test_directed_path(self):
G = nx.DiGraph([(0, 1), (1, 2)])
assert nx.is_simple_path(G, [0, 1, 2])
def test_directed_non_path(self):
G = nx.DiGraph([(0, 1), (1, 2)])
assert not nx.is_simple_path(G, [2, 1, 0])
def test_directed_cycle(self):
G = nx.DiGraph([(0, 1), (1, 2), (2, 0)])
assert not nx.is_simple_path(G, [0, 1, 2, 0])
def test_multigraph(self):
G = nx.MultiGraph([(0, 1), (0, 1)])
assert nx.is_simple_path(G, [0, 1])
def test_multidigraph(self):
G = nx.MultiDiGraph([(0, 1), (0, 1), (1, 0), (1, 0)])
assert nx.is_simple_path(G, [0, 1])
# Tests for all_simple_paths
def test_all_simple_paths():
G = nx.path_graph(4)
paths = nx.all_simple_paths(G, 0, 3)
assert {tuple(p) for p in paths} == {(0, 1, 2, 3)}
def test_all_simple_paths_with_two_targets_emits_two_paths():
G = nx.path_graph(4)
G.add_edge(2, 4)
paths = nx.all_simple_paths(G, 0, [3, 4])
assert {tuple(p) for p in paths} == {(0, 1, 2, 3), (0, 1, 2, 4)}
def test_digraph_all_simple_paths_with_two_targets_emits_two_paths():
G = nx.path_graph(4, create_using=nx.DiGraph())
G.add_edge(2, 4)
paths = nx.all_simple_paths(G, 0, [3, 4])
assert {tuple(p) for p in paths} == {(0, 1, 2, 3), (0, 1, 2, 4)}
def test_all_simple_paths_with_two_targets_cutoff():
G = nx.path_graph(4)
G.add_edge(2, 4)
paths = nx.all_simple_paths(G, 0, [3, 4], cutoff=3)
assert {tuple(p) for p in paths} == {(0, 1, 2, 3), (0, 1, 2, 4)}
def test_digraph_all_simple_paths_with_two_targets_cutoff():
G = nx.path_graph(4, create_using=nx.DiGraph())
G.add_edge(2, 4)
paths = nx.all_simple_paths(G, 0, [3, 4], cutoff=3)
assert {tuple(p) for p in paths} == {(0, 1, 2, 3), (0, 1, 2, 4)}
def test_all_simple_paths_with_two_targets_in_line_emits_two_paths():
G = nx.path_graph(4)
paths = nx.all_simple_paths(G, 0, [2, 3])
assert {tuple(p) for p in paths} == {(0, 1, 2), (0, 1, 2, 3)}
def test_all_simple_paths_ignores_cycle():
G = nx.cycle_graph(3, create_using=nx.DiGraph())
G.add_edge(1, 3)
paths = nx.all_simple_paths(G, 0, 3)
assert {tuple(p) for p in paths} == {(0, 1, 3)}
def test_all_simple_paths_with_two_targets_inside_cycle_emits_two_paths():
G = nx.cycle_graph(3, create_using=nx.DiGraph())
G.add_edge(1, 3)
paths = nx.all_simple_paths(G, 0, [2, 3])
assert {tuple(p) for p in paths} == {(0, 1, 2), (0, 1, 3)}
def test_all_simple_paths_source_target():
G = nx.path_graph(4)
paths = nx.all_simple_paths(G, 1, 1)
assert list(paths) == []
def test_all_simple_paths_cutoff():
G = nx.complete_graph(4)
paths = nx.all_simple_paths(G, 0, 1, cutoff=1)
assert {tuple(p) for p in paths} == {(0, 1)}
paths = nx.all_simple_paths(G, 0, 1, cutoff=2)
assert {tuple(p) for p in paths} == {(0, 1), (0, 2, 1), (0, 3, 1)}
def test_all_simple_paths_on_non_trivial_graph():
""" you may need to draw this graph to make sure it is reasonable """
G = nx.path_graph(5, create_using=nx.DiGraph())
G.add_edges_from([(0, 5), (1, 5), (1, 3), (5, 4), (4, 2), (4, 3)])
paths = nx.all_simple_paths(G, 1, [2, 3])
assert {tuple(p) for p in paths} == {
(1, 2),
(1, 3, 4, 2),
(1, 5, 4, 2),
(1, 3),
(1, 2, 3),
(1, 5, 4, 3),
(1, 5, 4, 2, 3),
}
paths = nx.all_simple_paths(G, 1, [2, 3], cutoff=3)
assert {tuple(p) for p in paths} == {
(1, 2),
(1, 3, 4, 2),
(1, 5, 4, 2),
(1, 3),
(1, 2, 3),
(1, 5, 4, 3),
}
paths = nx.all_simple_paths(G, 1, [2, 3], cutoff=2)
assert {tuple(p) for p in paths} == {(1, 2), (1, 3), (1, 2, 3)}
def test_all_simple_paths_multigraph():
G = nx.MultiGraph([(1, 2), (1, 2)])
paths = nx.all_simple_paths(G, 1, 1)
assert list(paths) == []
nx.add_path(G, [3, 1, 10, 2])
paths = list(nx.all_simple_paths(G, 1, 2))
assert len(paths) == 3
assert {tuple(p) for p in paths} == {(1, 2), (1, 2), (1, 10, 2)}
def test_all_simple_paths_multigraph_with_cutoff():
G = nx.MultiGraph([(1, 2), (1, 2), (1, 10), (10, 2)])
paths = list(nx.all_simple_paths(G, 1, 2, cutoff=1))
assert len(paths) == 2
assert {tuple(p) for p in paths} == {(1, 2), (1, 2)}
def test_all_simple_paths_directed():
G = nx.DiGraph()
nx.add_path(G, [1, 2, 3])
nx.add_path(G, [3, 2, 1])
paths = nx.all_simple_paths(G, 1, 3)
assert {tuple(p) for p in paths} == {(1, 2, 3)}
def test_all_simple_paths_empty():
G = nx.path_graph(4)
paths = nx.all_simple_paths(G, 0, 3, cutoff=2)
assert list(paths) == []
def test_all_simple_paths_corner_cases():
assert list(nx.all_simple_paths(nx.empty_graph(2), 0, 0)) == []
assert list(nx.all_simple_paths(nx.empty_graph(2), 0, 1)) == []
assert list(nx.all_simple_paths(nx.path_graph(9), 0, 8, 0)) == []
def hamiltonian_path(G, source):
source = arbitrary_element(G)
neighbors = set(G[source]) - {source}
n = len(G)
for target in neighbors:
for path in nx.all_simple_paths(G, source, target):
if len(path) == n:
yield path
def test_hamiltonian_path():
from itertools import permutations
G = nx.complete_graph(4)
paths = [list(p) for p in hamiltonian_path(G, 0)]
exact = [[0] + list(p) for p in permutations([1, 2, 3], 3)]
assert sorted(paths) == sorted(exact)
def test_cutoff_zero():
G = nx.complete_graph(4)
paths = nx.all_simple_paths(G, 0, 3, cutoff=0)
assert list(list(p) for p in paths) == []
paths = nx.all_simple_paths(nx.MultiGraph(G), 0, 3, cutoff=0)
assert list(list(p) for p in paths) == []
def test_source_missing():
with pytest.raises(nx.NodeNotFound):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
list(nx.all_simple_paths(nx.MultiGraph(G), 0, 3))
def test_target_missing():
with pytest.raises(nx.NodeNotFound):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
list(nx.all_simple_paths(nx.MultiGraph(G), 1, 4))
# Tests for all_simple_edge_paths
def test_all_simple_edge_paths():
G = nx.path_graph(4)
paths = nx.all_simple_edge_paths(G, 0, 3)
assert {tuple(p) for p in paths} == {((0, 1), (1, 2), (2, 3))}
def test_all_simple_edge_paths_with_two_targets_emits_two_paths():
G = nx.path_graph(4)
G.add_edge(2, 4)
paths = nx.all_simple_edge_paths(G, 0, [3, 4])
assert {tuple(p) for p in paths} == {
((0, 1), (1, 2), (2, 3)),
((0, 1), (1, 2), (2, 4)),
}
def test_digraph_all_simple_edge_paths_with_two_targets_emits_two_paths():
G = nx.path_graph(4, create_using=nx.DiGraph())
G.add_edge(2, 4)
paths = nx.all_simple_edge_paths(G, 0, [3, 4])
assert {tuple(p) for p in paths} == {
((0, 1), (1, 2), (2, 3)),
((0, 1), (1, 2), (2, 4)),
}
def test_all_simple_edge_paths_with_two_targets_cutoff():
G = nx.path_graph(4)
G.add_edge(2, 4)
paths = nx.all_simple_edge_paths(G, 0, [3, 4], cutoff=3)
assert {tuple(p) for p in paths} == {
((0, 1), (1, 2), (2, 3)),
((0, 1), (1, 2), (2, 4)),
}
def test_digraph_all_simple_edge_paths_with_two_targets_cutoff():
G = nx.path_graph(4, create_using=nx.DiGraph())
G.add_edge(2, 4)
paths = nx.all_simple_edge_paths(G, 0, [3, 4], cutoff=3)
assert {tuple(p) for p in paths} == {
((0, 1), (1, 2), (2, 3)),
((0, 1), (1, 2), (2, 4)),
}
def test_all_simple_edge_paths_with_two_targets_in_line_emits_two_paths():
G = nx.path_graph(4)
paths = nx.all_simple_edge_paths(G, 0, [2, 3])
assert {tuple(p) for p in paths} == {((0, 1), (1, 2)), ((0, 1), (1, 2), (2, 3))}
def test_all_simple_edge_paths_ignores_cycle():
G = nx.cycle_graph(3, create_using=nx.DiGraph())
G.add_edge(1, 3)
paths = nx.all_simple_edge_paths(G, 0, 3)
assert {tuple(p) for p in paths} == {((0, 1), (1, 3))}
def test_all_simple_edge_paths_with_two_targets_inside_cycle_emits_two_paths():
G = nx.cycle_graph(3, create_using=nx.DiGraph())
G.add_edge(1, 3)
paths = nx.all_simple_edge_paths(G, 0, [2, 3])
assert {tuple(p) for p in paths} == {((0, 1), (1, 2)), ((0, 1), (1, 3))}
def test_all_simple_edge_paths_source_target():
G = nx.path_graph(4)
paths = nx.all_simple_edge_paths(G, 1, 1)
assert list(paths) == []
def test_all_simple_edge_paths_cutoff():
G = nx.complete_graph(4)
paths = nx.all_simple_edge_paths(G, 0, 1, cutoff=1)
assert {tuple(p) for p in paths} == {((0, 1),)}
paths = nx.all_simple_edge_paths(G, 0, 1, cutoff=2)
assert {tuple(p) for p in paths} == {((0, 1),), ((0, 2), (2, 1)), ((0, 3), (3, 1))}
def test_all_simple_edge_paths_on_non_trivial_graph():
""" you may need to draw this graph to make sure it is reasonable """
G = nx.path_graph(5, create_using=nx.DiGraph())
G.add_edges_from([(0, 5), (1, 5), (1, 3), (5, 4), (4, 2), (4, 3)])
paths = nx.all_simple_edge_paths(G, 1, [2, 3])
assert {tuple(p) for p in paths} == {
((1, 2),),
((1, 3), (3, 4), (4, 2)),
((1, 5), (5, 4), (4, 2)),
((1, 3),),
((1, 2), (2, 3)),
((1, 5), (5, 4), (4, 3)),
((1, 5), (5, 4), (4, 2), (2, 3)),
}
paths = nx.all_simple_edge_paths(G, 1, [2, 3], cutoff=3)
assert {tuple(p) for p in paths} == {
((1, 2),),
((1, 3), (3, 4), (4, 2)),
((1, 5), (5, 4), (4, 2)),
((1, 3),),
((1, 2), (2, 3)),
((1, 5), (5, 4), (4, 3)),
}
paths = nx.all_simple_edge_paths(G, 1, [2, 3], cutoff=2)
assert {tuple(p) for p in paths} == {((1, 2),), ((1, 3),), ((1, 2), (2, 3))}
def test_all_simple_edge_paths_multigraph():
G = nx.MultiGraph([(1, 2), (1, 2)])
paths = nx.all_simple_edge_paths(G, 1, 1)
assert list(paths) == []
nx.add_path(G, [3, 1, 10, 2])
paths = list(nx.all_simple_edge_paths(G, 1, 2))
assert len(paths) == 3
assert {tuple(p) for p in paths} == {
((1, 2, 0),),
((1, 2, 1),),
((1, 10, 0), (10, 2, 0)),
}
def test_all_simple_edge_paths_multigraph_with_cutoff():
G = nx.MultiGraph([(1, 2), (1, 2), (1, 10), (10, 2)])
paths = list(nx.all_simple_edge_paths(G, 1, 2, cutoff=1))
assert len(paths) == 2
assert {tuple(p) for p in paths} == {((1, 2, 0),), ((1, 2, 1),)}
def test_all_simple_edge_paths_directed():
G = nx.DiGraph()
nx.add_path(G, [1, 2, 3])
nx.add_path(G, [3, 2, 1])
paths = nx.all_simple_edge_paths(G, 1, 3)
assert {tuple(p) for p in paths} == {((1, 2), (2, 3))}
def test_all_simple_edge_paths_empty():
G = nx.path_graph(4)
paths = nx.all_simple_edge_paths(G, 0, 3, cutoff=2)
assert list(paths) == []
def test_all_simple_edge_paths_corner_cases():
assert list(nx.all_simple_edge_paths(nx.empty_graph(2), 0, 0)) == []
assert list(nx.all_simple_edge_paths(nx.empty_graph(2), 0, 1)) == []
assert list(nx.all_simple_edge_paths(nx.path_graph(9), 0, 8, 0)) == []
def hamiltonian_edge_path(G, source):
source = arbitrary_element(G)
neighbors = set(G[source]) - {source}
n = len(G)
for target in neighbors:
for path in nx.all_simple_edge_paths(G, source, target):
if len(path) == n - 1:
yield path
def test_hamiltonian__edge_path():
from itertools import permutations
G = nx.complete_graph(4)
paths = hamiltonian_edge_path(G, 0)
exact = [list(pairwise([0] + list(p))) for p in permutations([1, 2, 3], 3)]
assert sorted(exact) == [p for p in sorted(paths)]
def test_edge_cutoff_zero():
G = nx.complete_graph(4)
paths = nx.all_simple_edge_paths(G, 0, 3, cutoff=0)
assert list(list(p) for p in paths) == []
paths = nx.all_simple_edge_paths(nx.MultiGraph(G), 0, 3, cutoff=0)
assert list(list(p) for p in paths) == []
def test_edge_source_missing():
with pytest.raises(nx.NodeNotFound):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
list(nx.all_simple_edge_paths(nx.MultiGraph(G), 0, 3))
def test_edge_target_missing():
with pytest.raises(nx.NodeNotFound):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
list(nx.all_simple_edge_paths(nx.MultiGraph(G), 1, 4))
# Tests for shortest_simple_paths
def test_shortest_simple_paths():
G = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
paths = nx.shortest_simple_paths(G, 1, 12)
assert next(paths) == [1, 2, 3, 4, 8, 12]
assert next(paths) == [1, 5, 6, 7, 8, 12]
assert [len(path) for path in nx.shortest_simple_paths(G, 1, 12)] == sorted(
[len(path) for path in nx.all_simple_paths(G, 1, 12)]
)
def test_shortest_simple_paths_directed():
G = nx.cycle_graph(7, create_using=nx.DiGraph())
paths = nx.shortest_simple_paths(G, 0, 3)
assert [path for path in paths] == [[0, 1, 2, 3]]
def test_shortest_simple_paths_directed_with_weight_fucntion():
def cost(u, v, x):
return 1
G = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
paths = nx.shortest_simple_paths(G, 1, 12)
assert next(paths) == [1, 2, 3, 4, 8, 12]
assert next(paths) == [1, 5, 6, 7, 8, 12]
assert [
len(path) for path in nx.shortest_simple_paths(G, 1, 12, weight=cost)
] == sorted([len(path) for path in nx.all_simple_paths(G, 1, 12)])
def test_shortest_simple_paths_with_weight_fucntion():
def cost(u, v, x):
return 1
G = nx.cycle_graph(7, create_using=nx.DiGraph())
paths = nx.shortest_simple_paths(G, 0, 3, weight=cost)
assert [path for path in paths] == [[0, 1, 2, 3]]
def test_Greg_Bernstein():
g1 = nx.Graph()
g1.add_nodes_from(["N0", "N1", "N2", "N3", "N4"])
g1.add_edge("N4", "N1", weight=10.0, capacity=50, name="L5")
g1.add_edge("N4", "N0", weight=7.0, capacity=40, name="L4")
g1.add_edge("N0", "N1", weight=10.0, capacity=45, name="L1")
g1.add_edge("N3", "N0", weight=10.0, capacity=50, name="L0")
g1.add_edge("N2", "N3", weight=12.0, capacity=30, name="L2")
g1.add_edge("N1", "N2", weight=15.0, capacity=42, name="L3")
solution = [["N1", "N0", "N3"], ["N1", "N2", "N3"], ["N1", "N4", "N0", "N3"]]
result = list(nx.shortest_simple_paths(g1, "N1", "N3", weight="weight"))
assert result == solution
def test_weighted_shortest_simple_path():
def cost_func(path):
return sum(G.adj[u][v]["weight"] for (u, v) in zip(path, path[1:]))
G = nx.complete_graph(5)
weight = {(u, v): random.randint(1, 100) for (u, v) in G.edges()}
nx.set_edge_attributes(G, weight, "weight")
cost = 0
for path in nx.shortest_simple_paths(G, 0, 3, weight="weight"):
this_cost = cost_func(path)
assert cost <= this_cost
cost = this_cost
def test_directed_weighted_shortest_simple_path():
def cost_func(path):
return sum(G.adj[u][v]["weight"] for (u, v) in zip(path, path[1:]))
G = nx.complete_graph(5)
G = G.to_directed()
weight = {(u, v): random.randint(1, 100) for (u, v) in G.edges()}
nx.set_edge_attributes(G, weight, "weight")
cost = 0
for path in nx.shortest_simple_paths(G, 0, 3, weight="weight"):
this_cost = cost_func(path)
assert cost <= this_cost
cost = this_cost
def test_weighted_shortest_simple_path_issue2427():
G = nx.Graph()
G.add_edge("IN", "OUT", weight=2)
G.add_edge("IN", "A", weight=1)
G.add_edge("IN", "B", weight=2)
G.add_edge("B", "OUT", weight=2)
assert list(nx.shortest_simple_paths(G, "IN", "OUT", weight="weight")) == [
["IN", "OUT"],
["IN", "B", "OUT"],
]
G = nx.Graph()
G.add_edge("IN", "OUT", weight=10)
G.add_edge("IN", "A", weight=1)
G.add_edge("IN", "B", weight=1)
G.add_edge("B", "OUT", weight=1)
assert list(nx.shortest_simple_paths(G, "IN", "OUT", weight="weight")) == [
["IN", "B", "OUT"],
["IN", "OUT"],
]
def test_directed_weighted_shortest_simple_path_issue2427():
G = nx.DiGraph()
G.add_edge("IN", "OUT", weight=2)
G.add_edge("IN", "A", weight=1)
G.add_edge("IN", "B", weight=2)
G.add_edge("B", "OUT", weight=2)
assert list(nx.shortest_simple_paths(G, "IN", "OUT", weight="weight")) == [
["IN", "OUT"],
["IN", "B", "OUT"],
]
G = nx.DiGraph()
G.add_edge("IN", "OUT", weight=10)
G.add_edge("IN", "A", weight=1)
G.add_edge("IN", "B", weight=1)
G.add_edge("B", "OUT", weight=1)
assert list(nx.shortest_simple_paths(G, "IN", "OUT", weight="weight")) == [
["IN", "B", "OUT"],
["IN", "OUT"],
]
def test_weight_name():
G = nx.cycle_graph(7)
nx.set_edge_attributes(G, 1, "weight")
nx.set_edge_attributes(G, 1, "foo")
G.adj[1][2]["foo"] = 7
paths = list(nx.shortest_simple_paths(G, 0, 3, weight="foo"))
solution = [[0, 6, 5, 4, 3], [0, 1, 2, 3]]
assert paths == solution
def test_ssp_source_missing():
with pytest.raises(nx.NodeNotFound):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
list(nx.shortest_simple_paths(G, 0, 3))
def test_ssp_target_missing():
with pytest.raises(nx.NodeNotFound):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
list(nx.shortest_simple_paths(G, 1, 4))
def test_ssp_multigraph():
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.MultiGraph()
nx.add_path(G, [1, 2, 3])
list(nx.shortest_simple_paths(G, 1, 4))
def test_ssp_source_missing2():
with pytest.raises(nx.NetworkXNoPath):
G = nx.Graph()
nx.add_path(G, [0, 1, 2])
nx.add_path(G, [3, 4, 5])
list(nx.shortest_simple_paths(G, 0, 3))
def test_bidirectional_shortest_path_restricted_cycle():
cycle = nx.cycle_graph(7)
length, path = _bidirectional_shortest_path(cycle, 0, 3)
assert path == [0, 1, 2, 3]
length, path = _bidirectional_shortest_path(cycle, 0, 3, ignore_nodes=[1])
assert path == [0, 6, 5, 4, 3]
def test_bidirectional_shortest_path_restricted_wheel():
wheel = nx.wheel_graph(6)
length, path = _bidirectional_shortest_path(wheel, 1, 3)
assert path in [[1, 0, 3], [1, 2, 3]]
length, path = _bidirectional_shortest_path(wheel, 1, 3, ignore_nodes=[0])
assert path == [1, 2, 3]
length, path = _bidirectional_shortest_path(wheel, 1, 3, ignore_nodes=[0, 2])
assert path == [1, 5, 4, 3]
length, path = _bidirectional_shortest_path(
wheel, 1, 3, ignore_edges=[(1, 0), (5, 0), (2, 3)]
)
assert path in [[1, 2, 0, 3], [1, 5, 4, 3]]
def test_bidirectional_shortest_path_restricted_directed_cycle():
directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
length, path = _bidirectional_shortest_path(directed_cycle, 0, 3)
assert path == [0, 1, 2, 3]
pytest.raises(
nx.NetworkXNoPath,
_bidirectional_shortest_path,
directed_cycle,
0,
3,
ignore_nodes=[1],
)
length, path = _bidirectional_shortest_path(
directed_cycle, 0, 3, ignore_edges=[(2, 1)]
)
assert path == [0, 1, 2, 3]
pytest.raises(
nx.NetworkXNoPath,
_bidirectional_shortest_path,
directed_cycle,
0,
3,
ignore_edges=[(1, 2)],
)
def test_bidirectional_shortest_path_ignore():
G = nx.Graph()
nx.add_path(G, [1, 2])
nx.add_path(G, [1, 3])
nx.add_path(G, [1, 4])
pytest.raises(
nx.NetworkXNoPath, _bidirectional_shortest_path, G, 1, 2, ignore_nodes=[1]
)
pytest.raises(
nx.NetworkXNoPath, _bidirectional_shortest_path, G, 1, 2, ignore_nodes=[2]
)
G = nx.Graph()
nx.add_path(G, [1, 3])
nx.add_path(G, [1, 4])
nx.add_path(G, [3, 2])
pytest.raises(
nx.NetworkXNoPath, _bidirectional_shortest_path, G, 1, 2, ignore_nodes=[1, 2]
)
def validate_path(G, s, t, soln_len, path):
assert path[0] == s
assert path[-1] == t
assert soln_len == sum(
G[u][v].get("weight", 1) for u, v in zip(path[:-1], path[1:])
)
def validate_length_path(G, s, t, soln_len, length, path):
assert soln_len == length
validate_path(G, s, t, length, path)
def test_bidirectional_dijksta_restricted():
XG = nx.DiGraph()
XG.add_weighted_edges_from(
[
("s", "u", 10),
("s", "x", 5),
("u", "v", 1),
("u", "x", 2),
("v", "y", 1),
("x", "u", 3),
("x", "v", 5),
("x", "y", 2),
("y", "s", 7),
("y", "v", 6),
]
)
XG3 = nx.Graph()
XG3.add_weighted_edges_from(
[[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
)
validate_length_path(XG, "s", "v", 9, *_bidirectional_dijkstra(XG, "s", "v"))
validate_length_path(
XG, "s", "v", 10, *_bidirectional_dijkstra(XG, "s", "v", ignore_nodes=["u"])
)
validate_length_path(
XG,
"s",
"v",
11,
*_bidirectional_dijkstra(XG, "s", "v", ignore_edges=[("s", "x")])
)
pytest.raises(
nx.NetworkXNoPath,
_bidirectional_dijkstra,
XG,
"s",
"v",
ignore_nodes=["u"],
ignore_edges=[("s", "x")],
)
validate_length_path(XG3, 0, 3, 15, *_bidirectional_dijkstra(XG3, 0, 3))
validate_length_path(
XG3, 0, 3, 16, *_bidirectional_dijkstra(XG3, 0, 3, ignore_nodes=[1])
)
validate_length_path(
XG3, 0, 3, 16, *_bidirectional_dijkstra(XG3, 0, 3, ignore_edges=[(2, 3)])
)
pytest.raises(
nx.NetworkXNoPath,
_bidirectional_dijkstra,
XG3,
0,
3,
ignore_nodes=[1],
ignore_edges=[(5, 4)],
)
def test_bidirectional_dijkstra_no_path():
with pytest.raises(nx.NetworkXNoPath):
G = nx.Graph()
nx.add_path(G, [1, 2, 3])
nx.add_path(G, [4, 5, 6])
_bidirectional_dijkstra(G, 1, 6)
def test_bidirectional_dijkstra_ignore():
G = nx.Graph()
nx.add_path(G, [1, 2, 10])
nx.add_path(G, [1, 3, 10])
pytest.raises(nx.NetworkXNoPath, _bidirectional_dijkstra, G, 1, 2, ignore_nodes=[1])
pytest.raises(nx.NetworkXNoPath, _bidirectional_dijkstra, G, 1, 2, ignore_nodes=[2])
pytest.raises(
nx.NetworkXNoPath, _bidirectional_dijkstra, G, 1, 2, ignore_nodes=[1, 2]
)

View file

@ -0,0 +1,59 @@
import pytest
numpy = pytest.importorskip("numpy")
import random
from networkx import random_reference, lattice_reference, sigma, omega
import networkx as nx
rng = random.Random(0)
rng = 42
def test_random_reference():
G = nx.connected_watts_strogatz_graph(50, 6, 0.1, seed=rng)
Gr = random_reference(G, niter=1, seed=rng)
C = nx.average_clustering(G)
Cr = nx.average_clustering(Gr)
assert C > Cr
pytest.raises(nx.NetworkXError, random_reference, nx.Graph())
pytest.raises(nx.NetworkXNotImplemented, random_reference, nx.DiGraph())
H = nx.Graph(((0, 1), (2, 3)))
Hl = random_reference(H, niter=1, seed=rng)
def test_lattice_reference():
G = nx.connected_watts_strogatz_graph(50, 6, 1, seed=rng)
Gl = lattice_reference(G, niter=1, seed=rng)
L = nx.average_shortest_path_length(G)
Ll = nx.average_shortest_path_length(Gl)
assert Ll > L
pytest.raises(nx.NetworkXError, lattice_reference, nx.Graph())
pytest.raises(nx.NetworkXNotImplemented, lattice_reference, nx.DiGraph())
H = nx.Graph(((0, 1), (2, 3)))
Hl = lattice_reference(H, niter=1)
def test_sigma():
Gs = nx.connected_watts_strogatz_graph(50, 6, 0.1, seed=rng)
Gr = nx.connected_watts_strogatz_graph(50, 6, 1, seed=rng)
sigmas = sigma(Gs, niter=1, nrand=2, seed=rng)
sigmar = sigma(Gr, niter=1, nrand=2, seed=rng)
assert sigmar < sigmas
def test_omega():
Gl = nx.connected_watts_strogatz_graph(50, 6, 0, seed=rng)
Gr = nx.connected_watts_strogatz_graph(50, 6, 1, seed=rng)
Gs = nx.connected_watts_strogatz_graph(50, 6, 0.1, seed=rng)
omegal = omega(Gl, niter=1, nrand=1, seed=rng)
omegar = omega(Gr, niter=1, nrand=1, seed=rng)
omegas = omega(Gs, niter=1, nrand=1, seed=rng)
print("omegas, omegal, omegar")
print(omegas, omegal, omegar)
assert omegal < omegas and omegas < omegar

View file

@ -0,0 +1,22 @@
import pytest
import networkx as nx
def test_smetric():
g = nx.Graph()
g.add_edge(1, 2)
g.add_edge(2, 3)
g.add_edge(2, 4)
g.add_edge(1, 4)
sm = nx.s_metric(g, normalized=False)
assert sm == 19.0
# smNorm = nx.s_metric(g,normalized=True)
# assert_equal(smNorm, 0.95)
def test_normalized():
with pytest.raises(nx.NetworkXError):
sm = nx.s_metric(nx.Graph(), normalized=True)

View file

@ -0,0 +1,137 @@
"""Unit tests for the sparsifier computation functions."""
import pytest
import networkx as nx
from networkx.utils import py_random_state
_seed = 2
def _test_spanner(G, spanner, stretch, weight=None):
"""Test whether a spanner is valid.
This function tests whether the given spanner is a subgraph of the
given graph G with the same node set. It also tests for all shortest
paths whether they adhere to the given stretch.
Parameters
----------
G : NetworkX graph
The original graph for which the spanner was constructed.
spanner : NetworkX graph
The spanner to be tested.
stretch : float
The proclaimed stretch of the spanner.
weight : object
The edge attribute to use as distance.
"""
# check node set
assert set(G.nodes()) == set(spanner.nodes())
# check edge set and weights
for u, v in spanner.edges():
assert G.has_edge(u, v)
if weight:
assert spanner[u][v][weight] == G[u][v][weight]
# check connectivity and stretch
original_length = dict(nx.shortest_path_length(G, weight=weight))
spanner_length = dict(nx.shortest_path_length(spanner, weight=weight))
for u in G.nodes():
for v in G.nodes():
if u in original_length and v in original_length[u]:
assert spanner_length[u][v] <= stretch * original_length[u][v]
@py_random_state(1)
def _assign_random_weights(G, seed=None):
"""Assigns random weights to the edges of a graph.
Parameters
----------
G : NetworkX graph
The original graph for which the spanner was constructed.
seed : integer, random_state, or None (default)
Indicator of random number generation state.
See :ref:`Randomness<randomness>`.
"""
for u, v in G.edges():
G[u][v]["weight"] = seed.random()
def test_spanner_trivial():
"""Test a trivial spanner with stretch 1."""
G = nx.complete_graph(20)
spanner = nx.spanner(G, 1, seed=_seed)
for u, v in G.edges:
assert spanner.has_edge(u, v)
def test_spanner_unweighted_complete_graph():
"""Test spanner construction on a complete unweighted graph."""
G = nx.complete_graph(20)
spanner = nx.spanner(G, 4, seed=_seed)
_test_spanner(G, spanner, 4)
spanner = nx.spanner(G, 10, seed=_seed)
_test_spanner(G, spanner, 10)
def test_spanner_weighted_complete_graph():
"""Test spanner construction on a complete weighted graph."""
G = nx.complete_graph(20)
_assign_random_weights(G, seed=_seed)
spanner = nx.spanner(G, 4, weight="weight", seed=_seed)
_test_spanner(G, spanner, 4, weight="weight")
spanner = nx.spanner(G, 10, weight="weight", seed=_seed)
_test_spanner(G, spanner, 10, weight="weight")
def test_spanner_unweighted_gnp_graph():
"""Test spanner construction on an unweighted gnp graph."""
G = nx.gnp_random_graph(20, 0.4, seed=_seed)
spanner = nx.spanner(G, 4, seed=_seed)
_test_spanner(G, spanner, 4)
spanner = nx.spanner(G, 10, seed=_seed)
_test_spanner(G, spanner, 10)
def test_spanner_weighted_gnp_graph():
"""Test spanner construction on an weighted gnp graph."""
G = nx.gnp_random_graph(20, 0.4, seed=_seed)
_assign_random_weights(G, seed=_seed)
spanner = nx.spanner(G, 4, weight="weight", seed=_seed)
_test_spanner(G, spanner, 4, weight="weight")
spanner = nx.spanner(G, 10, weight="weight", seed=_seed)
_test_spanner(G, spanner, 10, weight="weight")
def test_spanner_unweighted_disconnected_graph():
"""Test spanner construction on a disconnected graph."""
G = nx.disjoint_union(nx.complete_graph(10), nx.complete_graph(10))
spanner = nx.spanner(G, 4, seed=_seed)
_test_spanner(G, spanner, 4)
spanner = nx.spanner(G, 10, seed=_seed)
_test_spanner(G, spanner, 10)
def test_spanner_invalid_stretch():
"""Check whether an invalid stretch is caught."""
with pytest.raises(ValueError):
G = nx.empty_graph()
nx.spanner(G, 0)

View file

@ -0,0 +1,133 @@
"""Unit tests for the :mod:`networkx.algorithms.structuralholes` module."""
import math
import networkx as nx
from networkx.testing import almost_equal
class TestStructuralHoles:
"""Unit tests for computing measures of structural holes.
The expected values for these functions were originally computed using the
proprietary software `UCINET`_ and the free software `IGraph`_ , and then
computed by hand to make sure that the results are correct.
.. _UCINET: https://sites.google.com/site/ucinetsoftware/home
.. _IGraph: http://igraph.org/
"""
def setup(self):
self.D = nx.DiGraph()
self.D.add_edges_from([(0, 1), (0, 2), (1, 0), (2, 1)])
self.D_weights = {(0, 1): 2, (0, 2): 2, (1, 0): 1, (2, 1): 1}
# Example from http://www.analytictech.com/connections/v20(1)/holes.htm
self.G = nx.Graph()
self.G.add_edges_from(
[
("A", "B"),
("A", "F"),
("A", "G"),
("A", "E"),
("E", "G"),
("F", "G"),
("B", "G"),
("B", "D"),
("D", "G"),
("G", "C"),
]
)
self.G_weights = {
("A", "B"): 2,
("A", "F"): 3,
("A", "G"): 5,
("A", "E"): 2,
("E", "G"): 8,
("F", "G"): 3,
("B", "G"): 4,
("B", "D"): 1,
("D", "G"): 3,
("G", "C"): 10,
}
def test_constraint_directed(self):
constraint = nx.constraint(self.D)
assert almost_equal(constraint[0], 1.003, places=3)
assert almost_equal(constraint[1], 1.003, places=3)
assert almost_equal(constraint[2], 1.389, places=3)
def test_effective_size_directed(self):
effective_size = nx.effective_size(self.D)
assert almost_equal(effective_size[0], 1.167, places=3)
assert almost_equal(effective_size[1], 1.167, places=3)
assert almost_equal(effective_size[2], 1, places=3)
def test_constraint_weighted_directed(self):
D = self.D.copy()
nx.set_edge_attributes(D, self.D_weights, "weight")
constraint = nx.constraint(D, weight="weight")
assert almost_equal(constraint[0], 0.840, places=3)
assert almost_equal(constraint[1], 1.143, places=3)
assert almost_equal(constraint[2], 1.378, places=3)
def test_effective_size_weighted_directed(self):
D = self.D.copy()
nx.set_edge_attributes(D, self.D_weights, "weight")
effective_size = nx.effective_size(D, weight="weight")
assert almost_equal(effective_size[0], 1.567, places=3)
assert almost_equal(effective_size[1], 1.083, places=3)
assert almost_equal(effective_size[2], 1, places=3)
def test_constraint_undirected(self):
constraint = nx.constraint(self.G)
assert almost_equal(constraint["G"], 0.400, places=3)
assert almost_equal(constraint["A"], 0.595, places=3)
assert almost_equal(constraint["C"], 1, places=3)
def test_effective_size_undirected_borgatti(self):
effective_size = nx.effective_size(self.G)
assert almost_equal(effective_size["G"], 4.67, places=2)
assert almost_equal(effective_size["A"], 2.50, places=2)
assert almost_equal(effective_size["C"], 1, places=2)
def test_effective_size_undirected(self):
G = self.G.copy()
nx.set_edge_attributes(G, 1, "weight")
effective_size = nx.effective_size(G, weight="weight")
assert almost_equal(effective_size["G"], 4.67, places=2)
assert almost_equal(effective_size["A"], 2.50, places=2)
assert almost_equal(effective_size["C"], 1, places=2)
def test_constraint_weighted_undirected(self):
G = self.G.copy()
nx.set_edge_attributes(G, self.G_weights, "weight")
constraint = nx.constraint(G, weight="weight")
assert almost_equal(constraint["G"], 0.299, places=3)
assert almost_equal(constraint["A"], 0.795, places=3)
assert almost_equal(constraint["C"], 1, places=3)
def test_effective_size_weighted_undirected(self):
G = self.G.copy()
nx.set_edge_attributes(G, self.G_weights, "weight")
effective_size = nx.effective_size(G, weight="weight")
assert almost_equal(effective_size["G"], 5.47, places=2)
assert almost_equal(effective_size["A"], 2.47, places=2)
assert almost_equal(effective_size["C"], 1, places=2)
def test_constraint_isolated(self):
G = self.G.copy()
G.add_node(1)
constraint = nx.constraint(G)
assert math.isnan(constraint[1])
def test_effective_size_isolated(self):
G = self.G.copy()
G.add_node(1)
nx.set_edge_attributes(G, self.G_weights, "weight")
effective_size = nx.effective_size(G, weight="weight")
assert math.isnan(effective_size[1])
def test_effective_size_borgatti_isolated(self):
G = self.G.copy()
G.add_node(1)
effective_size = nx.effective_size(G)
assert math.isnan(effective_size[1])

View file

@ -0,0 +1,56 @@
import pytest
import networkx as nx
# import random
# random.seed(0)
def test_double_edge_swap():
graph = nx.barabasi_albert_graph(200, 1)
degrees = sorted(d for n, d in graph.degree())
G = nx.double_edge_swap(graph, 40)
assert degrees == sorted(d for n, d in graph.degree())
def test_double_edge_swap_seed():
graph = nx.barabasi_albert_graph(200, 1)
degrees = sorted(d for n, d in graph.degree())
G = nx.double_edge_swap(graph, 40, seed=1)
assert degrees == sorted(d for n, d in graph.degree())
def test_connected_double_edge_swap():
graph = nx.barabasi_albert_graph(200, 1)
degrees = sorted(d for n, d in graph.degree())
G = nx.connected_double_edge_swap(graph, 40, seed=1)
assert nx.is_connected(graph)
assert degrees == sorted(d for n, d in graph.degree())
def test_double_edge_swap_small():
with pytest.raises(nx.NetworkXError):
G = nx.double_edge_swap(nx.path_graph(3))
def test_double_edge_swap_tries():
with pytest.raises(nx.NetworkXError):
G = nx.double_edge_swap(nx.path_graph(10), nswap=1, max_tries=0)
def test_connected_double_edge_swap_small():
with pytest.raises(nx.NetworkXError):
G = nx.connected_double_edge_swap(nx.path_graph(3))
def test_connected_double_edge_swap_not_connected():
with pytest.raises(nx.NetworkXError):
G = nx.path_graph(3)
nx.add_path(G, [10, 11, 12])
G = nx.connected_double_edge_swap(G)
def test_degree_seq_c4():
G = nx.cycle_graph(4)
degrees = sorted(d for n, d in G.degree())
G = nx.double_edge_swap(G, 1, 100)
assert degrees == sorted(d for n, d in G.degree())

View file

@ -0,0 +1,278 @@
"""
Threshold Graphs
================
"""
import pytest
import networkx as nx
import networkx.algorithms.threshold as nxt
from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic
from networkx.testing import almost_equal
cnlti = nx.convert_node_labels_to_integers
class TestGeneratorThreshold:
def test_threshold_sequence_graph_test(self):
G = nx.star_graph(10)
assert nxt.is_threshold_graph(G)
assert nxt.is_threshold_sequence(list(d for n, d in G.degree()))
G = nx.complete_graph(10)
assert nxt.is_threshold_graph(G)
assert nxt.is_threshold_sequence(list(d for n, d in G.degree()))
deg = [3, 2, 2, 1, 1, 1]
assert not nxt.is_threshold_sequence(deg)
deg = [3, 2, 2, 1]
assert nxt.is_threshold_sequence(deg)
G = nx.generators.havel_hakimi_graph(deg)
assert nxt.is_threshold_graph(G)
def test_creation_sequences(self):
deg = [3, 2, 2, 1]
G = nx.generators.havel_hakimi_graph(deg)
with pytest.raises(ValueError):
nxt.creation_sequence(deg, with_labels=True, compact=True)
cs0 = nxt.creation_sequence(deg)
H0 = nxt.threshold_graph(cs0)
assert "".join(cs0) == "ddid"
cs1 = nxt.creation_sequence(deg, with_labels=True)
H1 = nxt.threshold_graph(cs1)
assert cs1 == [(1, "d"), (2, "d"), (3, "i"), (0, "d")]
cs2 = nxt.creation_sequence(deg, compact=True)
H2 = nxt.threshold_graph(cs2)
assert cs2 == [2, 1, 1]
assert "".join(nxt.uncompact(cs2)) == "ddid"
assert graph_could_be_isomorphic(H0, G)
assert graph_could_be_isomorphic(H0, H1)
assert graph_could_be_isomorphic(H0, H2)
def test_make_compact(self):
assert nxt.make_compact(["d", "d", "d", "i", "d", "d"]) == [3, 1, 2]
assert nxt.make_compact([3, 1, 2]) == [3, 1, 2]
assert pytest.raises(TypeError, nxt.make_compact, [3.0, 1.0, 2.0])
def test_uncompact(self):
assert nxt.uncompact([3, 1, 2]) == ["d", "d", "d", "i", "d", "d"]
assert nxt.uncompact(["d", "d", "i", "d"]) == ["d", "d", "i", "d"]
assert nxt.uncompact(
nxt.uncompact([(1, "d"), (2, "d"), (3, "i"), (0, "d")])
) == nxt.uncompact([(1, "d"), (2, "d"), (3, "i"), (0, "d")])
assert pytest.raises(TypeError, nxt.uncompact, [3.0, 1.0, 2.0])
def test_creation_sequence_to_weights(self):
assert nxt.creation_sequence_to_weights([3, 1, 2]) == [
0.5,
0.5,
0.5,
0.25,
0.75,
0.75,
]
assert pytest.raises(
TypeError, nxt.creation_sequence_to_weights, [3.0, 1.0, 2.0]
)
def test_weights_to_creation_sequence(self):
deg = [3, 2, 2, 1]
with pytest.raises(ValueError):
nxt.weights_to_creation_sequence(deg, with_labels=True, compact=True)
assert nxt.weights_to_creation_sequence(deg, with_labels=True) == [
(3, "d"),
(1, "d"),
(2, "d"),
(0, "d"),
]
assert nxt.weights_to_creation_sequence(deg, compact=True) == [4]
def test_find_alternating_4_cycle(self):
G = nx.Graph()
G.add_edge(1, 2)
assert not nxt.find_alternating_4_cycle(G)
def test_shortest_path(self):
deg = [3, 2, 2, 1]
G = nx.generators.havel_hakimi_graph(deg)
cs1 = nxt.creation_sequence(deg, with_labels=True)
for n, m in [(3, 0), (0, 3), (0, 2), (0, 1), (1, 3), (3, 1), (1, 2), (2, 3)]:
assert nxt.shortest_path(cs1, n, m) == nx.shortest_path(G, n, m)
spl = nxt.shortest_path_length(cs1, 3)
spl2 = nxt.shortest_path_length([t for v, t in cs1], 2)
assert spl == spl2
spld = {}
for j, pl in enumerate(spl):
n = cs1[j][0]
spld[n] = pl
assert spld == nx.single_source_shortest_path_length(G, 3)
assert nxt.shortest_path(["d", "d", "d", "i", "d", "d"], 1, 2) == [1, 2]
assert nxt.shortest_path([3, 1, 2], 1, 2) == [1, 2]
assert pytest.raises(TypeError, nxt.shortest_path, [3.0, 1.0, 2.0], 1, 2)
assert pytest.raises(ValueError, nxt.shortest_path, [3, 1, 2], "a", 2)
assert pytest.raises(ValueError, nxt.shortest_path, [3, 1, 2], 1, "b")
assert nxt.shortest_path([3, 1, 2], 1, 1) == [1]
def test_shortest_path_length(self):
assert nxt.shortest_path_length([3, 1, 2], 1) == [1, 0, 1, 2, 1, 1]
assert nxt.shortest_path_length(["d", "d", "d", "i", "d", "d"], 1) == [
1,
0,
1,
2,
1,
1,
]
assert nxt.shortest_path_length(("d", "d", "d", "i", "d", "d"), 1) == [
1,
0,
1,
2,
1,
1,
]
assert pytest.raises(TypeError, nxt.shortest_path, [3.0, 1.0, 2.0], 1)
def random_threshold_sequence(self):
assert len(nxt.random_threshold_sequence(10, 0.5)) == 10
assert nxt.random_threshold_sequence(10, 0.5, seed=42) == [
"d",
"i",
"d",
"d",
"d",
"i",
"i",
"i",
"d",
"d",
]
assert pytest.raises(ValueError, nxt.random_threshold_sequence, 10, 1.5)
def test_right_d_threshold_sequence(self):
assert nxt.right_d_threshold_sequence(3, 2) == ["d", "i", "d"]
assert pytest.raises(ValueError, nxt.right_d_threshold_sequence, 2, 3)
def test_left_d_threshold_sequence(self):
assert nxt.left_d_threshold_sequence(3, 2) == ["d", "i", "d"]
assert pytest.raises(ValueError, nxt.left_d_threshold_sequence, 2, 3)
def test_weights_thresholds(self):
wseq = [3, 4, 3, 3, 5, 6, 5, 4, 5, 6]
cs = nxt.weights_to_creation_sequence(wseq, threshold=10)
wseq = nxt.creation_sequence_to_weights(cs)
cs2 = nxt.weights_to_creation_sequence(wseq)
assert cs == cs2
wseq = nxt.creation_sequence_to_weights(nxt.uncompact([3, 1, 2, 3, 3, 2, 3]))
assert wseq == [
s * 0.125 for s in [4, 4, 4, 3, 5, 5, 2, 2, 2, 6, 6, 6, 1, 1, 7, 7, 7]
]
wseq = nxt.creation_sequence_to_weights([3, 1, 2, 3, 3, 2, 3])
assert wseq == [
s * 0.125 for s in [4, 4, 4, 3, 5, 5, 2, 2, 2, 6, 6, 6, 1, 1, 7, 7, 7]
]
wseq = nxt.creation_sequence_to_weights(list(enumerate("ddidiiidididi")))
assert wseq == [s * 0.1 for s in [5, 5, 4, 6, 3, 3, 3, 7, 2, 8, 1, 9, 0]]
wseq = nxt.creation_sequence_to_weights("ddidiiidididi")
assert wseq == [s * 0.1 for s in [5, 5, 4, 6, 3, 3, 3, 7, 2, 8, 1, 9, 0]]
wseq = nxt.creation_sequence_to_weights("ddidiiidididid")
ws = [s / float(12) for s in [6, 6, 5, 7, 4, 4, 4, 8, 3, 9, 2, 10, 1, 11]]
assert sum([abs(c - d) for c, d in zip(wseq, ws)]) < 1e-14
def test_finding_routines(self):
G = nx.Graph({1: [2], 2: [3], 3: [4], 4: [5], 5: [6]})
G.add_edge(2, 4)
G.add_edge(2, 5)
G.add_edge(2, 7)
G.add_edge(3, 6)
G.add_edge(4, 6)
# Alternating 4 cycle
assert nxt.find_alternating_4_cycle(G) == [1, 2, 3, 6]
# Threshold graph
TG = nxt.find_threshold_graph(G)
assert nxt.is_threshold_graph(TG)
assert sorted(TG.nodes()) == [1, 2, 3, 4, 5, 7]
cs = nxt.creation_sequence(dict(TG.degree()), with_labels=True)
assert nxt.find_creation_sequence(G) == cs
def test_fast_versions_properties_threshold_graphs(self):
cs = "ddiiddid"
G = nxt.threshold_graph(cs)
assert nxt.density("ddiiddid") == nx.density(G)
assert sorted(nxt.degree_sequence(cs)) == sorted(d for n, d in G.degree())
ts = nxt.triangle_sequence(cs)
assert ts == list(nx.triangles(G).values())
assert sum(ts) // 3 == nxt.triangles(cs)
c1 = nxt.cluster_sequence(cs)
c2 = list(nx.clustering(G).values())
assert almost_equal(sum([abs(c - d) for c, d in zip(c1, c2)]), 0)
b1 = nx.betweenness_centrality(G).values()
b2 = nxt.betweenness_sequence(cs)
assert sum([abs(c - d) for c, d in zip(b1, b2)]) < 1e-14
assert nxt.eigenvalues(cs) == [0, 1, 3, 3, 5, 7, 7, 8]
# Degree Correlation
assert abs(nxt.degree_correlation(cs) + 0.593038821954) < 1e-12
assert nxt.degree_correlation("diiiddi") == -0.8
assert nxt.degree_correlation("did") == -1.0
assert nxt.degree_correlation("ddd") == 1.0
assert nxt.eigenvalues("dddiii") == [0, 0, 0, 0, 3, 3]
assert nxt.eigenvalues("dddiiid") == [0, 1, 1, 1, 4, 4, 7]
def test_tg_creation_routines(self):
s = nxt.left_d_threshold_sequence(5, 7)
s = nxt.right_d_threshold_sequence(5, 7)
s1 = nxt.swap_d(s, 1.0, 1.0)
s1 = nxt.swap_d(s, 1.0, 1.0, seed=1)
def test_eigenvectors(self):
np = pytest.importorskip("numpy")
eigenval = np.linalg.eigvals
scipy = pytest.importorskip("scipy")
cs = "ddiiddid"
G = nxt.threshold_graph(cs)
(tgeval, tgevec) = nxt.eigenvectors(cs)
dot = np.dot
assert [abs(dot(lv, lv) - 1.0) < 1e-9 for lv in tgevec] == [True] * 8
lapl = nx.laplacian_matrix(G)
# tgev=[ dot(lv,dot(lapl,lv)) for lv in tgevec ]
# assert_true(sum([abs(c-d) for c,d in zip(tgev,tgeval)]) < 1e-9)
# tgev.sort()
# lev=list(eigenval(lapl))
# lev.sort()
# assert_true(sum([abs(c-d) for c,d in zip(tgev,lev)]) < 1e-9)
def test_create_using(self):
cs = "ddiiddid"
G = nxt.threshold_graph(cs)
assert pytest.raises(
nx.exception.NetworkXError,
nxt.threshold_graph,
cs,
create_using=nx.DiGraph(),
)
MG = nxt.threshold_graph(cs, create_using=nx.MultiGraph())
assert sorted(MG.edges()) == sorted(G.edges())

View file

@ -0,0 +1,132 @@
"""Unit tests for the :mod:`networkx.algorithms.tournament` module."""
from itertools import combinations
from networkx import DiGraph
from networkx.algorithms.tournament import is_reachable
from networkx.algorithms.tournament import is_strongly_connected
from networkx.algorithms.tournament import is_tournament
from networkx.algorithms.tournament import random_tournament
from networkx.algorithms.tournament import hamiltonian_path
class TestIsTournament:
"""Unit tests for the :func:`networkx.tournament.is_tournament`
function.
"""
def test_is_tournament(self):
G = DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0), (1, 3), (0, 2)])
assert is_tournament(G)
def test_self_loops(self):
"""A tournament must have no self-loops."""
G = DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0), (1, 3), (0, 2)])
G.add_edge(0, 0)
assert not is_tournament(G)
def test_missing_edges(self):
"""A tournament must not have any pair of nodes without at least
one edge joining the pair.
"""
G = DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0), (1, 3)])
assert not is_tournament(G)
def test_bidirectional_edges(self):
"""A tournament must not have any pair of nodes with greater
than one edge joining the pair.
"""
G = DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0), (1, 3), (0, 2)])
G.add_edge(1, 0)
assert not is_tournament(G)
class TestRandomTournament:
"""Unit tests for the :func:`networkx.tournament.random_tournament`
function.
"""
def test_graph_is_tournament(self):
for n in range(10):
G = random_tournament(5)
assert is_tournament(G)
def test_graph_is_tournament_seed(self):
for n in range(10):
G = random_tournament(5, seed=1)
assert is_tournament(G)
class TestHamiltonianPath:
"""Unit tests for the :func:`networkx.tournament.hamiltonian_path`
function.
"""
def test_path_is_hamiltonian(self):
G = DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0), (1, 3), (0, 2)])
path = hamiltonian_path(G)
assert len(path) == 4
assert all(v in G[u] for u, v in zip(path, path[1:]))
def test_hamiltonian_cycle(self):
"""Tests that :func:`networkx.tournament.hamiltonian_path`
returns a Hamiltonian cycle when provided a strongly connected
tournament.
"""
G = DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0), (1, 3), (0, 2)])
path = hamiltonian_path(G)
assert len(path) == 4
assert all(v in G[u] for u, v in zip(path, path[1:]))
assert path[0] in G[path[-1]]
class TestReachability:
"""Unit tests for the :func:`networkx.tournament.is_reachable`
function.
"""
def test_reachable_pair(self):
"""Tests for a reachable pair of nodes."""
G = DiGraph([(0, 1), (1, 2), (2, 0)])
assert is_reachable(G, 0, 2)
def test_same_node_is_reachable(self):
"""Tests that a node is always reachable from itself."""
# G is an arbitrary tournament on ten nodes.
G = DiGraph(sorted(p) for p in combinations(range(10), 2))
assert all(is_reachable(G, v, v) for v in G)
def test_unreachable_pair(self):
"""Tests for an unreachable pair of nodes."""
G = DiGraph([(0, 1), (0, 2), (1, 2)])
assert not is_reachable(G, 1, 0)
class TestStronglyConnected:
"""Unit tests for the
:func:`networkx.tournament.is_strongly_connected` function.
"""
def test_is_strongly_connected(self):
"""Tests for a strongly connected tournament."""
G = DiGraph([(0, 1), (1, 2), (2, 0)])
assert is_strongly_connected(G)
def test_not_strongly_connected(self):
"""Tests for a tournament that is not strongly connected."""
G = DiGraph([(0, 1), (0, 2), (1, 2)])
assert not is_strongly_connected(G)

Some files were not shown because too many files have changed in this diff Show more