Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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)
|
|
@ -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
|
|
@ -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
|
132
venv/Lib/site-packages/networkx/algorithms/tests/test_chains.py
Normal file
132
venv/Lib/site-packages/networkx/algorithms/tests/test_chains.py
Normal 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)
|
109
venv/Lib/site-packages/networkx/algorithms/tests/test_chordal.py
Normal file
109
venv/Lib/site-packages/networkx/algorithms/tests/test_chordal.py
Normal 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()
|
280
venv/Lib/site-packages/networkx/algorithms/tests/test_clique.py
Normal file
280
venv/Lib/site-packages/networkx/algorithms/tests/test_clique.py
Normal 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))
|
436
venv/Lib/site-packages/networkx/algorithms/tests/test_cluster.py
Normal file
436
venv/Lib/site-packages/networkx/algorithms/tests/test_cluster.py
Normal 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}
|
|
@ -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)
|
179
venv/Lib/site-packages/networkx/algorithms/tests/test_core.py
Normal file
179
venv/Lib/site-packages/networkx/algorithms/tests/test_core.py
Normal 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])
|
|
@ -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)})
|
165
venv/Lib/site-packages/networkx/algorithms/tests/test_cuts.py
Normal file
165
venv/Lib/site-packages/networkx/algorithms/tests/test_cuts.py
Normal 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
|
350
venv/Lib/site-packages/networkx/algorithms/tests/test_cycles.py
Normal file
350
venv/Lib/site-packages/networkx/algorithms/tests/test_cycles.py
Normal 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)
|
|
@ -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})
|
633
venv/Lib/site-packages/networkx/algorithms/tests/test_dag.py
Normal file
633
venv/Lib/site-packages/networkx/algorithms/tests/test_dag.py
Normal 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())
|
|
@ -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
|
|
@ -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)
|
|
@ -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])
|
|
@ -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})
|
|
@ -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
|
191
venv/Lib/site-packages/networkx/algorithms/tests/test_euler.py
Normal file
191
venv/Lib/site-packages/networkx/algorithms/tests/test_euler.py
Normal 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)))
|
|
@ -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
|
|
@ -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")
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)])
|
|
@ -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
|
|
@ -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)
|
|
@ -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),
|
||||
}
|
364
venv/Lib/site-packages/networkx/algorithms/tests/test_minors.py
Normal file
364
venv/Lib/site-packages/networkx/algorithms/tests/test_minors.py
Normal 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))
|
85
venv/Lib/site-packages/networkx/algorithms/tests/test_mis.py
Normal file
85
venv/Lib/site-packages/networkx/algorithms/tests/test_mis.py
Normal 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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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]
|
||||
)
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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])
|
|
@ -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())
|
|
@ -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())
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue