Fixed database typo and removed unnecessary class identifier.

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

View file

@ -0,0 +1,43 @@
import networkx as nx
from networkx.algorithms.approximation import average_clustering
# This approximation has to be be exact in regular graphs
# with no triangles or with all possible triangles.
def test_petersen():
# Actual coefficient is 0
G = nx.petersen_graph()
assert average_clustering(G, trials=int(len(G) / 2)) == nx.average_clustering(G)
def test_petersen_seed():
# Actual coefficient is 0
G = nx.petersen_graph()
assert average_clustering(
G, trials=int(len(G) / 2), seed=1
) == nx.average_clustering(G)
def test_tetrahedral():
# Actual coefficient is 1
G = nx.tetrahedral_graph()
assert average_clustering(G, trials=int(len(G) / 2)) == nx.average_clustering(G)
def test_dodecahedral():
# Actual coefficient is 0
G = nx.dodecahedral_graph()
assert average_clustering(G, trials=int(len(G) / 2)) == nx.average_clustering(G)
def test_empty():
G = nx.empty_graph(5)
assert average_clustering(G, trials=int(len(G) / 2)) == 0
def test_complete():
G = nx.complete_graph(5)
assert average_clustering(G, trials=int(len(G) / 2)) == 1
G = nx.complete_graph(7)
assert average_clustering(G, trials=int(len(G) / 2)) == 1

View file

@ -0,0 +1,107 @@
"""Unit tests for the :mod:`networkx.algorithms.approximation.clique`
module.
"""
import networkx as nx
from networkx.algorithms.approximation import max_clique
from networkx.algorithms.approximation import clique_removal
from networkx.algorithms.approximation import large_clique_size
def is_independent_set(G, nodes):
"""Returns True if and only if `nodes` is a clique in `G`.
`G` is a NetworkX graph. `nodes` is an iterable of nodes in
`G`.
"""
return G.subgraph(nodes).number_of_edges() == 0
def is_clique(G, nodes):
"""Returns True if and only if `nodes` is an independent set
in `G`.
`G` is an undirected simple graph. `nodes` is an iterable of
nodes in `G`.
"""
H = G.subgraph(nodes)
n = len(H)
return H.number_of_edges() == n * (n - 1) // 2
class TestCliqueRemoval:
"""Unit tests for the
:func:`~networkx.algorithms.approximation.clique_removal` function.
"""
def test_trivial_graph(self):
G = nx.trivial_graph()
independent_set, cliques = clique_removal(G)
assert is_independent_set(G, independent_set)
assert all(is_clique(G, clique) for clique in cliques)
# In fact, we should only have 1-cliques, that is, singleton nodes.
assert all(len(clique) == 1 for clique in cliques)
def test_complete_graph(self):
G = nx.complete_graph(10)
independent_set, cliques = clique_removal(G)
assert is_independent_set(G, independent_set)
assert all(is_clique(G, clique) for clique in cliques)
def test_barbell_graph(self):
G = nx.barbell_graph(10, 5)
independent_set, cliques = clique_removal(G)
assert is_independent_set(G, independent_set)
assert all(is_clique(G, clique) for clique in cliques)
class TestMaxClique:
"""Unit tests for the :func:`networkx.algorithms.approximation.max_clique`
function.
"""
def test_null_graph(self):
G = nx.null_graph()
assert len(max_clique(G)) == 0
def test_complete_graph(self):
graph = nx.complete_graph(30)
# this should return the entire graph
mc = max_clique(graph)
assert 30 == len(mc)
def test_maximal_by_cardinality(self):
"""Tests that the maximal clique is computed according to maximum
cardinality of the sets.
For more information, see pull request #1531.
"""
G = nx.complete_graph(5)
G.add_edge(4, 5)
clique = max_clique(G)
assert len(clique) > 1
G = nx.lollipop_graph(30, 2)
clique = max_clique(G)
assert len(clique) > 2
def test_large_clique_size():
G = nx.complete_graph(9)
nx.add_cycle(G, [9, 10, 11])
G.add_edge(8, 9)
G.add_edge(1, 12)
G.add_node(13)
assert large_clique_size(G) == 9
G.remove_node(5)
assert large_clique_size(G) == 8
G.remove_edge(2, 3)
assert large_clique_size(G) == 7

View file

@ -0,0 +1,199 @@
import pytest
import networkx as nx
from networkx.algorithms import approximation as approx
def test_global_node_connectivity():
# Figure 1 chapter on Connectivity
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),
]
)
assert 2 == approx.local_node_connectivity(G, 1, 11)
assert 2 == approx.node_connectivity(G)
assert 2 == approx.node_connectivity(G, 1, 11)
def test_white_harary1():
# Figure 1b white and harary (2001)
# A graph with high adhesion (edge connectivity) and low cohesion
# (node 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)
assert 1 == approx.node_connectivity(G)
def test_complete_graphs():
for n in range(5, 25, 5):
G = nx.complete_graph(n)
assert n - 1 == approx.node_connectivity(G)
assert n - 1 == approx.node_connectivity(G, 0, 3)
def test_empty_graphs():
for k in range(5, 25, 5):
G = nx.empty_graph(k)
assert 0 == approx.node_connectivity(G)
assert 0 == approx.node_connectivity(G, 0, 3)
def test_petersen():
G = nx.petersen_graph()
assert 3 == approx.node_connectivity(G)
assert 3 == approx.node_connectivity(G, 0, 5)
# Approximation fails with tutte graph
# def test_tutte():
# G = nx.tutte_graph()
# assert_equal(3, approx.node_connectivity(G))
def test_dodecahedral():
G = nx.dodecahedral_graph()
assert 3 == approx.node_connectivity(G)
assert 3 == approx.node_connectivity(G, 0, 5)
def test_octahedral():
G = nx.octahedral_graph()
assert 4 == approx.node_connectivity(G)
assert 4 == approx.node_connectivity(G, 0, 5)
# Approximation can fail with icosahedral graph depending
# on iteration order.
# def test_icosahedral():
# G=nx.icosahedral_graph()
# assert_equal(5, approx.node_connectivity(G))
# assert_equal(5, approx.node_connectivity(G, 0, 5))
def test_only_source():
G = nx.complete_graph(5)
pytest.raises(nx.NetworkXError, approx.node_connectivity, G, s=0)
def test_only_target():
G = nx.complete_graph(5)
pytest.raises(nx.NetworkXError, approx.node_connectivity, G, t=0)
def test_missing_source():
G = nx.path_graph(4)
pytest.raises(nx.NetworkXError, approx.node_connectivity, G, 10, 1)
def test_missing_target():
G = nx.path_graph(4)
pytest.raises(nx.NetworkXError, approx.node_connectivity, G, 1, 10)
def test_source_equals_target():
G = nx.complete_graph(5)
pytest.raises(nx.NetworkXError, approx.local_node_connectivity, G, 0, 0)
def test_directed_node_connectivity():
G = nx.cycle_graph(10, create_using=nx.DiGraph()) # only one direction
D = nx.cycle_graph(10).to_directed() # 2 reciprocal edges
assert 1 == approx.node_connectivity(G)
assert 1 == approx.node_connectivity(G, 1, 4)
assert 2 == approx.node_connectivity(D)
assert 2 == approx.node_connectivity(D, 1, 4)
class TestAllPairsNodeConnectivityApprox:
@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)
cls.directed_gnp = nx.gnp_random_graph(30, 0.1, directed=True)
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 = approx.all_pairs_node_connectivity(self.cycle)
for source in K_undir:
for target, k in K_undir[source].items():
assert k == 2
K_dir = approx.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 = approx.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 = approx.all_pairs_node_connectivity(self.path)
for source in K_undir:
for target, k in K_undir[source].items():
assert k == 1
K_dir = approx.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_cutoff(self):
for G in [self.K10, self.K5, self.K20]:
for mp in [2, 3, 4]:
paths = approx.all_pairs_node_connectivity(G, cutoff=mp)
for source in paths:
for target, K in paths[source].items():
assert K == mp
def test_all_pairs_connectivity_nbunch(self):
G = nx.complete_graph(5)
nbunch = [0, 2, 3]
C = approx.all_pairs_node_connectivity(G, nbunch=nbunch)
assert len(C) == len(nbunch)

View file

@ -0,0 +1,65 @@
import networkx as nx
from networkx.algorithms.approximation import min_weighted_dominating_set
from networkx.algorithms.approximation import min_edge_dominating_set
class TestMinWeightDominatingSet:
def test_min_weighted_dominating_set(self):
graph = nx.Graph()
graph.add_edge(1, 2)
graph.add_edge(1, 5)
graph.add_edge(2, 3)
graph.add_edge(2, 5)
graph.add_edge(3, 4)
graph.add_edge(3, 6)
graph.add_edge(5, 6)
vertices = {1, 2, 3, 4, 5, 6}
# due to ties, this might be hard to test tight bounds
dom_set = min_weighted_dominating_set(graph)
for vertex in vertices - dom_set:
neighbors = set(graph.neighbors(vertex))
assert len(neighbors & dom_set) > 0, "Non dominating set found!"
def test_star_graph(self):
"""Tests that an approximate dominating set for the star graph,
even when the center node does not have the smallest integer
label, gives just the center node.
For more information, see #1527.
"""
# Create a star graph in which the center node has the highest
# label instead of the lowest.
G = nx.star_graph(10)
G = nx.relabel_nodes(G, {0: 9, 9: 0})
assert min_weighted_dominating_set(G) == {9}
def test_min_edge_dominating_set(self):
graph = nx.path_graph(5)
dom_set = min_edge_dominating_set(graph)
# this is a crappy way to test, but good enough for now.
for edge in graph.edges():
if edge in dom_set:
continue
else:
u, v = edge
found = False
for dom_edge in dom_set:
found |= u == dom_edge[0] or u == dom_edge[1]
assert found, "Non adjacent edge found!"
graph = nx.complete_graph(10)
dom_set = min_edge_dominating_set(graph)
# this is a crappy way to test, but good enough for now.
for edge in graph.edges():
if edge in dom_set:
continue
else:
u, v = edge
found = False
for dom_edge in dom_set:
found |= u == dom_edge[0] or u == dom_edge[1]
assert found, "Non adjacent edge found!"

View file

@ -0,0 +1,8 @@
import networkx as nx
import networkx.algorithms.approximation as a
def test_independent_set():
# smoke test
G = nx.Graph()
assert len(a.maximum_independent_set(G)) == 0

View file

@ -0,0 +1,300 @@
# Test for approximation to k-components algorithm
import pytest
import networkx as nx
from networkx.algorithms.approximation import k_components
from networkx.algorithms.approximation.kcomponents import _AntiGraph, _same
def build_k_number_dict(k_components):
k_num = {}
for k, comps in sorted(k_components.items()):
for comp in comps:
for node in comp:
k_num[node] = k
return k_num
##
# Some nice synthetic graphs
##
def graph_example_1():
G = nx.convert_node_labels_to_integers(
nx.grid_graph([5, 5]), label_attribute="labels"
)
rlabels = nx.get_node_attributes(G, "labels")
labels = {v: k for k, v in rlabels.items()}
for nodes in [
(labels[(0, 0)], labels[(1, 0)]),
(labels[(0, 4)], labels[(1, 4)]),
(labels[(3, 0)], labels[(4, 0)]),
(labels[(3, 4)], labels[(4, 4)]),
]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
G = nx.disjoint_union(G, P)
# Add two edges between the grid and P
G.add_edge(new_node + 1, nodes[0])
G.add_edge(new_node, nodes[1])
# K5 is 4-connected
K = nx.complete_graph(5)
G = nx.disjoint_union(G, K)
# Add three edges between P and K5
G.add_edge(new_node + 2, new_node + 11)
G.add_edge(new_node + 3, new_node + 12)
G.add_edge(new_node + 4, new_node + 13)
# Add another K5 sharing a node
G = nx.disjoint_union(G, K)
nbrs = G[new_node + 10]
G.remove_node(new_node + 10)
for nbr in nbrs:
G.add_edge(new_node + 17, nbr)
G.add_edge(new_node + 16, new_node + 5)
return G
def torrents_and_ferraro_graph():
G = nx.convert_node_labels_to_integers(
nx.grid_graph([5, 5]), label_attribute="labels"
)
rlabels = nx.get_node_attributes(G, "labels")
labels = {v: k for k, v in rlabels.items()}
for nodes in [(labels[(0, 4)], labels[(1, 4)]), (labels[(3, 4)], labels[(4, 4)])]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
G = nx.disjoint_union(G, P)
# Add two edges between the grid and P
G.add_edge(new_node + 1, nodes[0])
G.add_edge(new_node, nodes[1])
# K5 is 4-connected
K = nx.complete_graph(5)
G = nx.disjoint_union(G, K)
# Add three edges between P and K5
G.add_edge(new_node + 2, new_node + 11)
G.add_edge(new_node + 3, new_node + 12)
G.add_edge(new_node + 4, new_node + 13)
# Add another K5 sharing a node
G = nx.disjoint_union(G, K)
nbrs = G[new_node + 10]
G.remove_node(new_node + 10)
for nbr in nbrs:
G.add_edge(new_node + 17, nbr)
# Commenting this makes the graph not biconnected !!
# This stupid mistake make one reviewer very angry :P
G.add_edge(new_node + 16, new_node + 8)
for nodes in [(labels[(0, 0)], labels[(1, 0)]), (labels[(3, 0)], labels[(4, 0)])]:
new_node = G.order() + 1
# Petersen graph is triconnected
P = nx.petersen_graph()
G = nx.disjoint_union(G, P)
# Add two edges between the grid and P
G.add_edge(new_node + 1, nodes[0])
G.add_edge(new_node, nodes[1])
# K5 is 4-connected
K = nx.complete_graph(5)
G = nx.disjoint_union(G, K)
# Add three edges between P and K5
G.add_edge(new_node + 2, new_node + 11)
G.add_edge(new_node + 3, new_node + 12)
G.add_edge(new_node + 4, new_node + 13)
# Add another K5 sharing two nodes
G = nx.disjoint_union(G, K)
nbrs = G[new_node + 10]
G.remove_node(new_node + 10)
for nbr in nbrs:
G.add_edge(new_node + 17, nbr)
nbrs2 = G[new_node + 9]
G.remove_node(new_node + 9)
for nbr in nbrs2:
G.add_edge(new_node + 18, nbr)
return G
# Helper function
def _check_connectivity(G):
result = k_components(G)
for k, components in result.items():
if k < 3:
continue
for component in components:
C = G.subgraph(component)
K = nx.node_connectivity(C)
assert K >= k
def test_torrents_and_ferraro_graph():
G = torrents_and_ferraro_graph()
_check_connectivity(G)
def test_example_1():
G = graph_example_1()
_check_connectivity(G)
def test_karate_0():
G = nx.karate_club_graph()
_check_connectivity(G)
def test_karate_1():
karate_k_num = {
0: 4,
1: 4,
2: 4,
3: 4,
4: 3,
5: 3,
6: 3,
7: 4,
8: 4,
9: 2,
10: 3,
11: 1,
12: 2,
13: 4,
14: 2,
15: 2,
16: 2,
17: 2,
18: 2,
19: 3,
20: 2,
21: 2,
22: 2,
23: 3,
24: 3,
25: 3,
26: 2,
27: 3,
28: 3,
29: 3,
30: 4,
31: 3,
32: 4,
33: 4,
}
approx_karate_k_num = karate_k_num.copy()
approx_karate_k_num[24] = 2
approx_karate_k_num[25] = 2
G = nx.karate_club_graph()
k_comps = k_components(G)
k_num = build_k_number_dict(k_comps)
assert k_num in (karate_k_num, approx_karate_k_num)
def test_example_1_detail_3_and_4():
G = graph_example_1()
result = k_components(G)
# In this example graph there are 8 3-components, 4 with 15 nodes
# and 4 with 5 nodes.
assert len(result[3]) == 8
assert len([c for c in result[3] if len(c) == 15]) == 4
assert len([c for c in result[3] if len(c) == 5]) == 4
# There are also 8 4-components all with 5 nodes.
assert len(result[4]) == 8
assert all(len(c) == 5 for c in result[4])
# Finally check that the k-components detected have actually node
# connectivity >= k.
for k, components in result.items():
if k < 3:
continue
for component in components:
K = nx.node_connectivity(G.subgraph(component))
assert K >= k
def test_directed():
with pytest.raises(nx.NetworkXNotImplemented):
G = nx.gnp_random_graph(10, 0.4, directed=True)
kc = k_components(G)
def test_same():
equal = {"A": 2, "B": 2, "C": 2}
slightly_different = {"A": 2, "B": 1, "C": 2}
different = {"A": 2, "B": 8, "C": 18}
assert _same(equal)
assert not _same(slightly_different)
assert _same(slightly_different, tol=1)
assert not _same(different)
assert not _same(different, tol=4)
class TestAntiGraph:
@classmethod
def setup_class(cls):
cls.Gnp = nx.gnp_random_graph(20, 0.8)
cls.Anp = _AntiGraph(nx.complement(cls.Gnp))
cls.Gd = nx.davis_southern_women_graph()
cls.Ad = _AntiGraph(nx.complement(cls.Gd))
cls.Gk = nx.karate_club_graph()
cls.Ak = _AntiGraph(nx.complement(cls.Gk))
cls.GA = [(cls.Gnp, cls.Anp), (cls.Gd, cls.Ad), (cls.Gk, cls.Ak)]
def test_size(self):
for G, A in self.GA:
n = G.order()
s = len(list(G.edges())) + len(list(A.edges()))
assert s == (n * (n - 1)) / 2
def test_degree(self):
for G, A in self.GA:
assert sorted(G.degree()) == sorted(A.degree())
def test_core_number(self):
for G, A in self.GA:
assert nx.core_number(G) == nx.core_number(A)
def test_connected_components(self):
for G, A in self.GA:
gc = [set(c) for c in nx.connected_components(G)]
ac = [set(c) for c in nx.connected_components(A)]
for comp in ac:
assert comp in gc
def test_adj(self):
for G, A in self.GA:
for n, nbrs in G.adj.items():
a_adj = sorted((n, sorted(ad)) for n, ad in A.adj.items())
g_adj = sorted((n, sorted(ad)) for n, ad in G.adj.items())
assert a_adj == g_adj
def test_adjacency(self):
for G, A in self.GA:
a_adj = list(A.adjacency())
for n, nbrs in G.adjacency():
assert (n, set(nbrs)) in a_adj
def test_neighbors(self):
for G, A in self.GA:
node = list(G.nodes())[0]
assert set(G.neighbors(node)) == set(A.neighbors(node))
def test_node_not_in_graph(self):
for G, A in self.GA:
node = "non_existent_node"
pytest.raises(nx.NetworkXError, A.neighbors, node)
pytest.raises(nx.NetworkXError, G.neighbors, node)
def test_degree_thingraph(self):
for G, A in self.GA:
node = list(G.nodes())[0]
nodes = list(G.nodes())[1:4]
assert G.degree(node) == A.degree(node)
assert sum(d for n, d in G.degree()) == sum(d for n, d in A.degree())
# AntiGraph is a ThinGraph, so all the weights are 1
assert sum(d for n, d in A.degree()) == sum(
d for n, d in A.degree(weight="weight")
)
assert sum(d for n, d in G.degree(nodes)) == sum(
d for n, d in A.degree(nodes)
)

View file

@ -0,0 +1,8 @@
import networkx as nx
import networkx.algorithms.approximation as a
def test_min_maximal_matching():
# smoke test
G = nx.Graph()
assert len(a.min_maximal_matching(G)) == 0

View file

@ -0,0 +1,31 @@
import networkx as nx
import networkx.algorithms.approximation as apxa
def test_ramsey():
# this should only find the complete graph
graph = nx.complete_graph(10)
c, i = apxa.ramsey_R2(graph)
cdens = nx.density(graph.subgraph(c))
assert cdens == 1.0, "clique not correctly found by ramsey!"
idens = nx.density(graph.subgraph(i))
assert idens == 0.0, "i-set not correctly found by ramsey!"
# this trival graph has no cliques. should just find i-sets
graph = nx.trivial_graph()
c, i = apxa.ramsey_R2(graph)
assert c == {0}, "clique not correctly found by ramsey!"
assert i == {0}, "i-set not correctly found by ramsey!"
graph = nx.barbell_graph(10, 5, nx.Graph())
c, i = apxa.ramsey_R2(graph)
cdens = nx.density(graph.subgraph(c))
assert cdens == 1.0, "clique not correctly found by ramsey!"
idens = nx.density(graph.subgraph(i))
assert idens == 0.0, "i-set not correctly found by ramsey!"
# add self-loops and test again
graph.add_edges_from([(n, n) for n in range(0, len(graph), 2)])
cc, ii = apxa.ramsey_R2(graph)
assert cc == c
assert ii == i

View file

@ -0,0 +1,83 @@
import pytest
import networkx as nx
from networkx.algorithms.approximation.steinertree import metric_closure
from networkx.algorithms.approximation.steinertree import steiner_tree
from networkx.testing.utils import assert_edges_equal
class TestSteinerTree:
@classmethod
def setup_class(cls):
G = nx.Graph()
G.add_edge(1, 2, weight=10)
G.add_edge(2, 3, weight=10)
G.add_edge(3, 4, weight=10)
G.add_edge(4, 5, weight=10)
G.add_edge(5, 6, weight=10)
G.add_edge(2, 7, weight=1)
G.add_edge(7, 5, weight=1)
cls.G = G
cls.term_nodes = [1, 2, 3, 4, 5]
def test_connected_metric_closure(self):
G = self.G.copy()
G.add_node(100)
pytest.raises(nx.NetworkXError, metric_closure, G)
def test_metric_closure(self):
M = metric_closure(self.G)
mc = [
(1, 2, {"distance": 10, "path": [1, 2]}),
(1, 3, {"distance": 20, "path": [1, 2, 3]}),
(1, 4, {"distance": 22, "path": [1, 2, 7, 5, 4]}),
(1, 5, {"distance": 12, "path": [1, 2, 7, 5]}),
(1, 6, {"distance": 22, "path": [1, 2, 7, 5, 6]}),
(1, 7, {"distance": 11, "path": [1, 2, 7]}),
(2, 3, {"distance": 10, "path": [2, 3]}),
(2, 4, {"distance": 12, "path": [2, 7, 5, 4]}),
(2, 5, {"distance": 2, "path": [2, 7, 5]}),
(2, 6, {"distance": 12, "path": [2, 7, 5, 6]}),
(2, 7, {"distance": 1, "path": [2, 7]}),
(3, 4, {"distance": 10, "path": [3, 4]}),
(3, 5, {"distance": 12, "path": [3, 2, 7, 5]}),
(3, 6, {"distance": 22, "path": [3, 2, 7, 5, 6]}),
(3, 7, {"distance": 11, "path": [3, 2, 7]}),
(4, 5, {"distance": 10, "path": [4, 5]}),
(4, 6, {"distance": 20, "path": [4, 5, 6]}),
(4, 7, {"distance": 11, "path": [4, 5, 7]}),
(5, 6, {"distance": 10, "path": [5, 6]}),
(5, 7, {"distance": 1, "path": [5, 7]}),
(6, 7, {"distance": 11, "path": [6, 5, 7]}),
]
assert_edges_equal(list(M.edges(data=True)), mc)
def test_steiner_tree(self):
S = steiner_tree(self.G, self.term_nodes)
expected_steiner_tree = [
(1, 2, {"weight": 10}),
(2, 3, {"weight": 10}),
(2, 7, {"weight": 1}),
(3, 4, {"weight": 10}),
(5, 7, {"weight": 1}),
]
assert_edges_equal(list(S.edges(data=True)), expected_steiner_tree)
def test_multigraph_steiner_tree(self):
G = nx.MultiGraph()
G.add_edges_from(
[
(1, 2, 0, {"weight": 1}),
(2, 3, 0, {"weight": 999}),
(2, 3, 1, {"weight": 1}),
(3, 4, 0, {"weight": 1}),
(3, 5, 0, {"weight": 1}),
]
)
terminal_nodes = [2, 4, 5]
expected_edges = [
(2, 3, 1, {"weight": 1}), # edge with key 1 has lower weight
(3, 4, 0, {"weight": 1}),
(3, 5, 0, {"weight": 1}),
]
T = steiner_tree(G, terminal_nodes)
assert_edges_equal(T.edges(data=True, keys=True), expected_edges)

View file

@ -0,0 +1,269 @@
import networkx as nx
from networkx.algorithms.approximation import treewidth_min_degree
from networkx.algorithms.approximation import treewidth_min_fill_in
from networkx.algorithms.approximation.treewidth import min_fill_in_heuristic
from networkx.algorithms.approximation.treewidth import MinDegreeHeuristic
import itertools
def is_tree_decomp(graph, decomp):
"""Check if the given tree decomposition is valid."""
for x in graph.nodes():
appear_once = False
for bag in decomp.nodes():
if x in bag:
appear_once = True
break
assert appear_once
# Check if each connected pair of nodes are at least once together in a bag
for (x, y) in graph.edges():
appear_together = False
for bag in decomp.nodes():
if x in bag and y in bag:
appear_together = True
break
assert appear_together
# Check if the nodes associated with vertex v form a connected subset of T
for v in graph.nodes():
subset = []
for bag in decomp.nodes():
if v in bag:
subset.append(bag)
sub_graph = decomp.subgraph(subset)
assert nx.is_connected(sub_graph)
class TestTreewidthMinDegree:
"""Unit tests for the min_degree function"""
@classmethod
def setup_class(cls):
"""Setup for different kinds of trees"""
cls.complete = nx.Graph()
cls.complete.add_edge(1, 2)
cls.complete.add_edge(2, 3)
cls.complete.add_edge(1, 3)
cls.small_tree = nx.Graph()
cls.small_tree.add_edge(1, 3)
cls.small_tree.add_edge(4, 3)
cls.small_tree.add_edge(2, 3)
cls.small_tree.add_edge(3, 5)
cls.small_tree.add_edge(5, 6)
cls.small_tree.add_edge(5, 7)
cls.small_tree.add_edge(6, 7)
cls.deterministic_graph = nx.Graph()
cls.deterministic_graph.add_edge(0, 1) # deg(0) = 1
cls.deterministic_graph.add_edge(1, 2) # deg(1) = 2
cls.deterministic_graph.add_edge(2, 3)
cls.deterministic_graph.add_edge(2, 4) # deg(2) = 3
cls.deterministic_graph.add_edge(3, 4)
cls.deterministic_graph.add_edge(3, 5)
cls.deterministic_graph.add_edge(3, 6) # deg(3) = 4
cls.deterministic_graph.add_edge(4, 5)
cls.deterministic_graph.add_edge(4, 6)
cls.deterministic_graph.add_edge(4, 7) # deg(4) = 5
cls.deterministic_graph.add_edge(5, 6)
cls.deterministic_graph.add_edge(5, 7)
cls.deterministic_graph.add_edge(5, 8)
cls.deterministic_graph.add_edge(5, 9) # deg(5) = 6
cls.deterministic_graph.add_edge(6, 7)
cls.deterministic_graph.add_edge(6, 8)
cls.deterministic_graph.add_edge(6, 9) # deg(6) = 6
cls.deterministic_graph.add_edge(7, 8)
cls.deterministic_graph.add_edge(7, 9) # deg(7) = 5
cls.deterministic_graph.add_edge(8, 9) # deg(8) = 4
def test_petersen_graph(self):
"""Test Petersen graph tree decomposition result"""
G = nx.petersen_graph()
_, decomp = treewidth_min_degree(G)
is_tree_decomp(G, decomp)
def test_small_tree_treewidth(self):
"""Test small tree
Test if the computed treewidth of the known self.small_tree is 2.
As we know which value we can expect from our heuristic, values other
than two are regressions
"""
G = self.small_tree
# the order of removal should be [1,2,4]3[5,6,7]
# (with [] denoting any order of the containing nodes)
# resulting in treewidth 2 for the heuristic
treewidth, _ = treewidth_min_fill_in(G)
assert treewidth == 2
def test_heuristic_abort(self):
"""Test heuristic abort condition for fully connected graph"""
graph = {}
for u in self.complete:
graph[u] = set()
for v in self.complete[u]:
if u != v: # ignore self-loop
graph[u].add(v)
deg_heuristic = MinDegreeHeuristic(graph)
node = deg_heuristic.best_node(graph)
if node is None:
pass
else:
assert False
def test_empty_graph(self):
"""Test empty graph"""
G = nx.Graph()
_, _ = treewidth_min_degree(G)
def test_two_component_graph(self):
"""Test empty graph"""
G = nx.Graph()
G.add_node(1)
G.add_node(2)
treewidth, _ = treewidth_min_degree(G)
assert treewidth == 0
def test_heuristic_first_steps(self):
"""Test first steps of min_degree heuristic"""
graph = {
n: set(self.deterministic_graph[n]) - {n} for n in self.deterministic_graph
}
deg_heuristic = MinDegreeHeuristic(graph)
elim_node = deg_heuristic.best_node(graph)
print(f"Graph {graph}:")
steps = []
while elim_node is not None:
print(f"Removing {elim_node}:")
steps.append(elim_node)
nbrs = graph[elim_node]
for u, v in itertools.permutations(nbrs, 2):
if v not in graph[u]:
graph[u].add(v)
for u in graph:
if elim_node in graph[u]:
graph[u].remove(elim_node)
del graph[elim_node]
print(f"Graph {graph}:")
elim_node = deg_heuristic.best_node(graph)
# check only the first 5 elements for equality
assert steps[:5] == [0, 1, 2, 3, 4]
class TestTreewidthMinFillIn:
"""Unit tests for the treewidth_min_fill_in function."""
@classmethod
def setup_class(cls):
"""Setup for different kinds of trees"""
cls.complete = nx.Graph()
cls.complete.add_edge(1, 2)
cls.complete.add_edge(2, 3)
cls.complete.add_edge(1, 3)
cls.small_tree = nx.Graph()
cls.small_tree.add_edge(1, 2)
cls.small_tree.add_edge(2, 3)
cls.small_tree.add_edge(3, 4)
cls.small_tree.add_edge(1, 4)
cls.small_tree.add_edge(2, 4)
cls.small_tree.add_edge(4, 5)
cls.small_tree.add_edge(5, 6)
cls.small_tree.add_edge(5, 7)
cls.small_tree.add_edge(6, 7)
cls.deterministic_graph = nx.Graph()
cls.deterministic_graph.add_edge(1, 2)
cls.deterministic_graph.add_edge(1, 3)
cls.deterministic_graph.add_edge(3, 4)
cls.deterministic_graph.add_edge(2, 4)
cls.deterministic_graph.add_edge(3, 5)
cls.deterministic_graph.add_edge(4, 5)
cls.deterministic_graph.add_edge(3, 6)
cls.deterministic_graph.add_edge(5, 6)
def test_petersen_graph(self):
"""Test Petersen graph tree decomposition result"""
G = nx.petersen_graph()
_, decomp = treewidth_min_fill_in(G)
is_tree_decomp(G, decomp)
def test_small_tree_treewidth(self):
"""Test if the computed treewidth of the known self.small_tree is 2"""
G = self.small_tree
# the order of removal should be [1,2,4]3[5,6,7]
# (with [] denoting any order of the containing nodes)
# resulting in treewidth 2 for the heuristic
treewidth, _ = treewidth_min_fill_in(G)
assert treewidth == 2
def test_heuristic_abort(self):
"""Test if min_fill_in returns None for fully connected graph"""
graph = {}
for u in self.complete:
graph[u] = set()
for v in self.complete[u]:
if u != v: # ignore self-loop
graph[u].add(v)
next_node = min_fill_in_heuristic(graph)
if next_node is None:
pass
else:
assert False
def test_empty_graph(self):
"""Test empty graph"""
G = nx.Graph()
_, _ = treewidth_min_fill_in(G)
def test_two_component_graph(self):
"""Test empty graph"""
G = nx.Graph()
G.add_node(1)
G.add_node(2)
treewidth, _ = treewidth_min_fill_in(G)
assert treewidth == 0
def test_heuristic_first_steps(self):
"""Test first steps of min_fill_in heuristic"""
graph = {
n: set(self.deterministic_graph[n]) - {n} for n in self.deterministic_graph
}
print(f"Graph {graph}:")
elim_node = min_fill_in_heuristic(graph)
steps = []
while elim_node is not None:
print(f"Removing {elim_node}:")
steps.append(elim_node)
nbrs = graph[elim_node]
for u, v in itertools.permutations(nbrs, 2):
if v not in graph[u]:
graph[u].add(v)
for u in graph:
if elim_node in graph[u]:
graph[u].remove(elim_node)
del graph[elim_node]
print(f"Graph {graph}:")
elim_node = min_fill_in_heuristic(graph)
# check only the first 2 elements for equality
assert steps[:2] == [6, 5]

View file

@ -0,0 +1,55 @@
import networkx as nx
from networkx.algorithms.approximation import min_weighted_vertex_cover
def is_cover(G, node_cover):
return all({u, v} & node_cover for u, v in G.edges())
class TestMWVC:
"""Unit tests for the approximate minimum weighted vertex cover
function,
:func:`~networkx.algorithms.approximation.vertex_cover.min_weighted_vertex_cover`.
"""
def test_unweighted_directed(self):
# Create a star graph in which half the nodes are directed in
# and half are directed out.
G = nx.DiGraph()
G.add_edges_from((0, v) for v in range(1, 26))
G.add_edges_from((v, 0) for v in range(26, 51))
cover = min_weighted_vertex_cover(G)
assert 2 == len(cover)
assert is_cover(G, cover)
def test_unweighted_undirected(self):
# create a simple star graph
size = 50
sg = nx.star_graph(size)
cover = min_weighted_vertex_cover(sg)
assert 2 == len(cover)
assert is_cover(sg, cover)
def test_weighted(self):
wg = nx.Graph()
wg.add_node(0, weight=10)
wg.add_node(1, weight=1)
wg.add_node(2, weight=1)
wg.add_node(3, weight=1)
wg.add_node(4, weight=1)
wg.add_edge(0, 1)
wg.add_edge(0, 2)
wg.add_edge(0, 3)
wg.add_edge(0, 4)
wg.add_edge(1, 2)
wg.add_edge(2, 3)
wg.add_edge(3, 4)
wg.add_edge(4, 1)
cover = min_weighted_vertex_cover(wg, weight="weight")
csum = sum(wg.nodes[node]["weight"] for node in cover)
assert 4 == csum
assert is_cover(wg, cover)