369 lines
14 KiB
Python
369 lines
14 KiB
Python
import itertools
|
|
import pytest
|
|
|
|
import networkx as nx
|
|
from networkx.algorithms import flow
|
|
from networkx.algorithms.connectivity import local_edge_connectivity
|
|
from networkx.algorithms.connectivity import local_node_connectivity
|
|
|
|
flow_funcs = [
|
|
flow.boykov_kolmogorov,
|
|
flow.dinitz,
|
|
flow.edmonds_karp,
|
|
flow.preflow_push,
|
|
flow.shortest_augmenting_path,
|
|
]
|
|
|
|
|
|
msg = "Assertion failed in function: {0}"
|
|
|
|
# helper functions for tests
|
|
|
|
|
|
def _generate_no_biconnected(max_attempts=50):
|
|
attempts = 0
|
|
while True:
|
|
G = nx.fast_gnp_random_graph(100, 0.0575, seed=42)
|
|
if nx.is_connected(G) and not nx.is_biconnected(G):
|
|
attempts = 0
|
|
yield G
|
|
else:
|
|
if attempts >= max_attempts:
|
|
msg = "Tried %d times: no suitable Graph."
|
|
raise Exception(msg % max_attempts)
|
|
else:
|
|
attempts += 1
|
|
|
|
|
|
def test_average_connectivity():
|
|
# figure 1 from:
|
|
# Beineke, L., O. Oellermann, and R. Pippert (2002). The average
|
|
# connectivity of a graph. Discrete mathematics 252(1-3), 31-45
|
|
# http://www.sciencedirect.com/science/article/pii/S0012365X01001807
|
|
G1 = nx.path_graph(3)
|
|
G1.add_edges_from([(1, 3), (1, 4)])
|
|
G2 = nx.path_graph(3)
|
|
G2.add_edges_from([(1, 3), (1, 4), (0, 3), (0, 4), (3, 4)])
|
|
G3 = nx.Graph()
|
|
for flow_func in flow_funcs:
|
|
kwargs = dict(flow_func=flow_func)
|
|
assert nx.average_node_connectivity(G1, **kwargs) == 1, msg.format(flow_func.__name__)
|
|
assert nx.average_node_connectivity(G2, **kwargs) == 2.2, msg.format(flow_func.__name__)
|
|
assert nx.average_node_connectivity(G3, **kwargs) == 0, msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_average_connectivity_directed():
|
|
G = nx.DiGraph([(1, 3), (1, 4), (1, 5)])
|
|
for flow_func in flow_funcs:
|
|
assert nx.average_node_connectivity(G) == 0.25, msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_articulation_points():
|
|
Ggen = _generate_no_biconnected()
|
|
for flow_func in flow_funcs:
|
|
for i in range(3):
|
|
G = next(Ggen)
|
|
assert nx.node_connectivity(G, flow_func=flow_func) == 1, msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_brandes_erlebach():
|
|
# Figure 1 chapter 7: Connectivity
|
|
# http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
|
|
G = nx.Graph()
|
|
G.add_edges_from([(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 6), (3, 4),
|
|
(3, 6), (4, 6), (4, 7), (5, 7), (6, 8), (6, 9), (7, 8),
|
|
(7, 10), (8, 11), (9, 10), (9, 11), (10, 11)])
|
|
for flow_func in flow_funcs:
|
|
kwargs = dict(flow_func=flow_func)
|
|
assert 3 == local_edge_connectivity(G, 1, 11, **kwargs), msg.format(flow_func.__name__)
|
|
assert 3 == nx.edge_connectivity(G, 1, 11, **kwargs), msg.format(flow_func.__name__)
|
|
assert 2 == local_node_connectivity(G, 1, 11, **kwargs), msg.format(flow_func.__name__)
|
|
assert 2 == nx.node_connectivity(G, 1, 11, **kwargs), msg.format(flow_func.__name__)
|
|
assert 2 == nx.edge_connectivity(G, **kwargs), msg.format(flow_func.__name__)
|
|
assert 2 == nx.node_connectivity(G, **kwargs), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_white_harary_1():
|
|
# Figure 1b white and harary (2001)
|
|
# # http://eclectic.ss.uci.edu/~drwhite/sm-w23.PDF
|
|
# A graph with high adhesion (edge connectivity) and low cohesion
|
|
# (vertex connectivity)
|
|
G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
|
|
G.remove_node(7)
|
|
for i in range(4, 7):
|
|
G.add_edge(0, i)
|
|
G = nx.disjoint_union(G, nx.complete_graph(4))
|
|
G.remove_node(G.order() - 1)
|
|
for i in range(7, 10):
|
|
G.add_edge(0, i)
|
|
for flow_func in flow_funcs:
|
|
assert 1 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_white_harary_2():
|
|
# Figure 8 white and harary (2001)
|
|
# # http://eclectic.ss.uci.edu/~drwhite/sm-w23.PDF
|
|
G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
|
|
G.add_edge(0, 4)
|
|
# kappa <= lambda <= delta
|
|
assert 3 == min(nx.core_number(G).values())
|
|
for flow_func in flow_funcs:
|
|
assert 1 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 1 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_complete_graphs():
|
|
for n in range(5, 20, 5):
|
|
for flow_func in flow_funcs:
|
|
G = nx.complete_graph(n)
|
|
assert n - 1 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert n - 1 == nx.node_connectivity(G.to_directed(),
|
|
flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert n - 1 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert n - 1 == nx.edge_connectivity(G.to_directed(),
|
|
flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_empty_graphs():
|
|
for k in range(5, 25, 5):
|
|
G = nx.empty_graph(k)
|
|
for flow_func in flow_funcs:
|
|
assert 0 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 0 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_petersen():
|
|
G = nx.petersen_graph()
|
|
for flow_func in flow_funcs:
|
|
assert 3 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_tutte():
|
|
G = nx.tutte_graph()
|
|
for flow_func in flow_funcs:
|
|
assert 3 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_dodecahedral():
|
|
G = nx.dodecahedral_graph()
|
|
for flow_func in flow_funcs:
|
|
assert 3 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 3 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_octahedral():
|
|
G = nx.octahedral_graph()
|
|
for flow_func in flow_funcs:
|
|
assert 4 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 4 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_icosahedral():
|
|
G = nx.icosahedral_graph()
|
|
for flow_func in flow_funcs:
|
|
assert 5 == nx.node_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 5 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_missing_source():
|
|
G = nx.path_graph(4)
|
|
for flow_func in flow_funcs:
|
|
pytest.raises(nx.NetworkXError, nx.node_connectivity, G, 10, 1,
|
|
flow_func=flow_func)
|
|
|
|
|
|
def test_missing_target():
|
|
G = nx.path_graph(4)
|
|
for flow_func in flow_funcs:
|
|
pytest.raises(nx.NetworkXError, nx.node_connectivity, G, 1, 10,
|
|
flow_func=flow_func)
|
|
|
|
|
|
def test_edge_missing_source():
|
|
G = nx.path_graph(4)
|
|
for flow_func in flow_funcs:
|
|
pytest.raises(nx.NetworkXError, nx.edge_connectivity, G, 10, 1,
|
|
flow_func=flow_func)
|
|
|
|
|
|
def test_edge_missing_target():
|
|
G = nx.path_graph(4)
|
|
for flow_func in flow_funcs:
|
|
pytest.raises(nx.NetworkXError, nx.edge_connectivity, G, 1, 10,
|
|
flow_func=flow_func)
|
|
|
|
|
|
def test_not_weakly_connected():
|
|
G = nx.DiGraph()
|
|
nx.add_path(G, [1, 2, 3])
|
|
nx.add_path(G, [4, 5])
|
|
for flow_func in flow_funcs:
|
|
assert nx.node_connectivity(G) == 0, msg.format(flow_func.__name__)
|
|
assert nx.edge_connectivity(G) == 0, msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_not_connected():
|
|
G = nx.Graph()
|
|
nx.add_path(G, [1, 2, 3])
|
|
nx.add_path(G, [4, 5])
|
|
for flow_func in flow_funcs:
|
|
assert nx.node_connectivity(G) == 0, msg.format(flow_func.__name__)
|
|
assert nx.edge_connectivity(G) == 0, msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_directed_edge_connectivity():
|
|
G = nx.cycle_graph(10, create_using=nx.DiGraph()) # only one direction
|
|
D = nx.cycle_graph(10).to_directed() # 2 reciprocal edges
|
|
for flow_func in flow_funcs:
|
|
assert 1 == nx.edge_connectivity(G, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 1 == local_edge_connectivity(G, 1, 4, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 1 == nx.edge_connectivity(G, 1, 4, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 2 == nx.edge_connectivity(D, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 2 == local_edge_connectivity(D, 1, 4, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
assert 2 == nx.edge_connectivity(D, 1, 4, flow_func=flow_func), msg.format(flow_func.__name__)
|
|
|
|
|
|
def test_cutoff():
|
|
G = nx.complete_graph(5)
|
|
for local_func in [local_edge_connectivity, local_node_connectivity]:
|
|
for flow_func in flow_funcs:
|
|
if flow_func is flow.preflow_push:
|
|
# cutoff is not supported by preflow_push
|
|
continue
|
|
for cutoff in [3, 2, 1]:
|
|
result = local_func(G, 0, 4, flow_func=flow_func, cutoff=cutoff)
|
|
assert cutoff == result, "cutoff error in {0}".format(flow_func.__name__)
|
|
|
|
|
|
def test_invalid_auxiliary():
|
|
G = nx.complete_graph(5)
|
|
pytest.raises(nx.NetworkXError, local_node_connectivity, G, 0, 3,
|
|
auxiliary=G)
|
|
|
|
|
|
def test_interface_only_source():
|
|
G = nx.complete_graph(5)
|
|
for interface_func in [nx.node_connectivity, nx.edge_connectivity]:
|
|
pytest.raises(nx.NetworkXError, interface_func, G, s=0)
|
|
|
|
|
|
def test_interface_only_target():
|
|
G = nx.complete_graph(5)
|
|
for interface_func in [nx.node_connectivity, nx.edge_connectivity]:
|
|
pytest.raises(nx.NetworkXError, interface_func, G, t=3)
|
|
|
|
|
|
def test_edge_connectivity_flow_vs_stoer_wagner():
|
|
graph_funcs = [
|
|
nx.icosahedral_graph,
|
|
nx.octahedral_graph,
|
|
nx.dodecahedral_graph,
|
|
]
|
|
for graph_func in graph_funcs:
|
|
G = graph_func()
|
|
assert nx.stoer_wagner(G)[0] == nx.edge_connectivity(G)
|
|
|
|
|
|
class TestAllPairsNodeConnectivity:
|
|
|
|
@classmethod
|
|
def setup_class(cls):
|
|
cls.path = nx.path_graph(7)
|
|
cls.directed_path = nx.path_graph(7, create_using=nx.DiGraph())
|
|
cls.cycle = nx.cycle_graph(7)
|
|
cls.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
|
|
cls.gnp = nx.gnp_random_graph(30, 0.1, seed=42)
|
|
cls.directed_gnp = nx.gnp_random_graph(30, 0.1, directed=True, seed=42)
|
|
cls.K20 = nx.complete_graph(20)
|
|
cls.K10 = nx.complete_graph(10)
|
|
cls.K5 = nx.complete_graph(5)
|
|
cls.G_list = [cls.path, cls.directed_path, cls.cycle,
|
|
cls.directed_cycle, cls.gnp, cls.directed_gnp,
|
|
cls.K10, cls.K5, cls.K20]
|
|
|
|
def test_cycles(self):
|
|
K_undir = nx.all_pairs_node_connectivity(self.cycle)
|
|
for source in K_undir:
|
|
for target, k in K_undir[source].items():
|
|
assert k == 2
|
|
K_dir = nx.all_pairs_node_connectivity(self.directed_cycle)
|
|
for source in K_dir:
|
|
for target, k in K_dir[source].items():
|
|
assert k == 1
|
|
|
|
def test_complete(self):
|
|
for G in [self.K10, self.K5, self.K20]:
|
|
K = nx.all_pairs_node_connectivity(G)
|
|
for source in K:
|
|
for target, k in K[source].items():
|
|
assert k == len(G) - 1
|
|
|
|
def test_paths(self):
|
|
K_undir = nx.all_pairs_node_connectivity(self.path)
|
|
for source in K_undir:
|
|
for target, k in K_undir[source].items():
|
|
assert k == 1
|
|
K_dir = nx.all_pairs_node_connectivity(self.directed_path)
|
|
for source in K_dir:
|
|
for target, k in K_dir[source].items():
|
|
if source < target:
|
|
assert k == 1
|
|
else:
|
|
assert k == 0
|
|
|
|
def test_all_pairs_connectivity_nbunch(self):
|
|
G = nx.complete_graph(5)
|
|
nbunch = [0, 2, 3]
|
|
C = nx.all_pairs_node_connectivity(G, nbunch=nbunch)
|
|
assert len(C) == len(nbunch)
|
|
|
|
def test_all_pairs_connectivity_icosahedral(self):
|
|
G = nx.icosahedral_graph()
|
|
C = nx.all_pairs_node_connectivity(G)
|
|
assert all(5 == C[u][v] for u, v in itertools.combinations(G, 2))
|
|
|
|
def test_all_pairs_connectivity(self):
|
|
G = nx.Graph()
|
|
nodes = [0, 1, 2, 3]
|
|
nx.add_path(G, nodes)
|
|
A = {n: {} for n in G}
|
|
for u, v in itertools.combinations(nodes, 2):
|
|
A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
|
|
C = nx.all_pairs_node_connectivity(G)
|
|
assert (sorted((k, sorted(v)) for k, v in A.items()) ==
|
|
sorted((k, sorted(v)) for k, v in C.items()))
|
|
|
|
def test_all_pairs_connectivity_directed(self):
|
|
G = nx.DiGraph()
|
|
nodes = [0, 1, 2, 3]
|
|
nx.add_path(G, nodes)
|
|
A = {n: {} for n in G}
|
|
for u, v in itertools.permutations(nodes, 2):
|
|
A[u][v] = nx.node_connectivity(G, u, v)
|
|
C = nx.all_pairs_node_connectivity(G)
|
|
assert (sorted((k, sorted(v)) for k, v in A.items()) ==
|
|
sorted((k, sorted(v)) for k, v in C.items()))
|
|
|
|
def test_all_pairs_connectivity_nbunch_combinations(self):
|
|
G = nx.complete_graph(5)
|
|
nbunch = [0, 2, 3]
|
|
A = {n: {} for n in nbunch}
|
|
for u, v in itertools.combinations(nbunch, 2):
|
|
A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
|
|
C = nx.all_pairs_node_connectivity(G, nbunch=nbunch)
|
|
assert (sorted((k, sorted(v)) for k, v in A.items()) ==
|
|
sorted((k, sorted(v)) for k, v in C.items()))
|
|
|
|
def test_all_pairs_connectivity_nbunch_iter(self):
|
|
G = nx.complete_graph(5)
|
|
nbunch = [0, 2, 3]
|
|
A = {n: {} for n in nbunch}
|
|
for u, v in itertools.combinations(nbunch, 2):
|
|
A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
|
|
C = nx.all_pairs_node_connectivity(G, nbunch=iter(nbunch))
|
|
assert (sorted((k, sorted(v)) for k, v in A.items()) ==
|
|
sorted((k, sorted(v)) for k, v in C.items()))
|