This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/networkx/algorithms/approximation/tests/test_treewidth.py
2020-07-30 01:16:18 +02:00

272 lines
9.1 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (C) 2018 by
# Rudolf-Andreas Floren <rudi.floren@gmail.com>
# Dominik Meier <dominik.meier@rwth-aachen.de>
# All rights reserved.
# BSD license.
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(object):
"""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]) - set([n])
for n in self.deterministic_graph}
deg_heuristic = MinDegreeHeuristic(graph)
elim_node = deg_heuristic.best_node(graph)
print("Graph {}:".format(graph))
steps = []
while elim_node is not None:
print("Removing {}:".format(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("Graph {}:".format(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(object):
"""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]) - set([n])
for n in self.deterministic_graph}
print("Graph {}:".format(graph))
elim_node = min_fill_in_heuristic(graph)
steps = []
while elim_node is not None:
print("Removing {}:".format(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("Graph {}:".format(graph))
elim_node = min_fill_in_heuristic(graph)
# check only the first 2 elements for equality
assert steps[:2] == [6, 5]