Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve nice tree decomp bug in #36843, and allow label_nice_tree_decomposition to return a digraph #36846

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 78 additions & 28 deletions src/sage/graphs/graph_decompositions/tree_decomposition.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ def make_nice_tree_decomposition(graph, tree_decomp):
INPUT:

- ``graph`` -- a Sage graph

- ``tree_decomp`` -- a tree decomposition

OUTPUT:
Expand Down Expand Up @@ -853,6 +854,46 @@ def make_nice_tree_decomposition(graph, tree_decomp):
sage: bip_one_four_TD = bip_one_four.treewidth(certificate=True)
sage: make_nice_tree_decomposition(bip_one_four, bip_one_four_TD)
Nice tree decomposition of Tree decomposition: Graph on 15 vertices

Check that :issue:`36843` is fixed::

sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
sage: triangle = graphs.CompleteGraph(3)
sage: triangle_TD = triangle.treewidth(certificate=True)
sage: make_nice_tree_decomposition(triangle, triangle_TD)
Nice tree decomposition of Tree decomposition: Graph on 7 vertices

::

sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
sage: graph = graphs.CompleteBipartiteGraph(2, 5)
sage: graph_TD = graph.treewidth(certificate=True)
sage: make_nice_tree_decomposition(graph, graph_TD)
Nice tree decomposition of Tree decomposition: Graph on 25 vertices
guojing0 marked this conversation as resolved.
Show resolved Hide resolved

::

sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
sage: empty_graph = graphs.EmptyGraph()
sage: tree_decomp = empty_graph.treewidth(certificate=True)
sage: len(make_nice_tree_decomposition(empty_graph, tree_decomp))
0

::

sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
sage: singleton = graphs.CompleteGraph(1)
sage: tree_decomp = singleton.treewidth(certificate=True)
sage: make_nice_tree_decomposition(singleton, tree_decomp)
Nice tree decomposition of Tree decomposition: Graph on 3 vertices

::

sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
sage: an_edge = graphs.CompleteGraph(2)
sage: tree_decomp = an_edge.treewidth(certificate=True)
sage: make_nice_tree_decomposition(an_edge, tree_decomp)
Nice tree decomposition of Tree decomposition: Graph on 5 vertices
"""
if not is_valid_tree_decomposition(graph, tree_decomp):
raise ValueError("input must be a valid tree decomposition for this graph")
Expand All @@ -864,11 +905,19 @@ def make_nice_tree_decomposition(graph, tree_decomp):

# Step 1: Ensure the tree is directed and has a root
# Choose a root and orient the edges from root-to-leaves direction
leaves = [u for u in tree_decomp if tree_decomp.degree(u) == 1]
root = leaves.pop()
#
# Testing <= 1 for the special case when one bag containing all vertices
leaves = [u for u in tree_decomp if tree_decomp.degree(u) <= 1]

from sage.graphs.digraph import DiGraph
directed_tree = DiGraph(tree_decomp.breadth_first_search(start=root, edges=True),
format='list_of_edges')
if len(leaves) == 1:
root = leaves[0]
directed_tree = DiGraph(tree_decomp)
else:
root = leaves.pop()

directed_tree = DiGraph(tree_decomp.breadth_first_search(start=root, edges=True),
format='list_of_edges')

# Relabel the graph in range (0, |tree_decomp| - 1)
bags_to_int = directed_tree.relabel(inplace=True, return_map=True)
Expand Down Expand Up @@ -905,7 +954,7 @@ def make_nice_tree_decomposition(graph, tree_decomp):
children = directed_tree.neighbors_out(ui)
children.pop() # one vertex remains a child of ui

directed_tree.delete_edges((ui, vi) for v in children)
directed_tree.delete_edges((ui, vi) for vi in children)

new_nodes = [directed_tree.add_vertex() for _ in range(len(children) - 1)]

Expand Down Expand Up @@ -1012,7 +1061,8 @@ def make_nice_tree_decomposition(graph, tree_decomp):

return nice_tree_decomp

def label_nice_tree_decomposition(nice_TD, root):

def label_nice_tree_decomposition(nice_TD, root, directed=False):
guojing0 marked this conversation as resolved.
Show resolved Hide resolved
r"""
Return a nice tree decomposition with nodes labelled accordingly.

Expand All @@ -1022,52 +1072,50 @@ def label_nice_tree_decomposition(nice_TD, root):

- ``root`` -- the root of the nice tree decomposition

- ``directed`` -- boolean (default: ``False``); whether to return the nice
tree decomposition as a directed graph rooted at vertex ``root`` or as an
undirected graph

OUTPUT:

A nice tree decomposition with nodes labelled.

EXAMPLES::

sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition, label_nice_tree_decomposition
sage: bip_one_four = graphs.CompleteBipartiteGraph(1, 4)
sage: bip_one_four_TD = bip_one_four.treewidth(certificate=True)
sage: nice_TD = make_nice_tree_decomposition(bip_one_four, bip_one_four_TD)
sage: claw = graphs.CompleteBipartiteGraph(1, 3)
sage: claw_TD = claw.treewidth(certificate=True)
sage: nice_TD = make_nice_tree_decomposition(claw, claw_TD)
sage: root = sorted(nice_TD.vertices())[0]
sage: label_TD = label_nice_tree_decomposition(nice_TD, root)
sage: for node in sorted(label_TD):
sage: label_TD = label_nice_tree_decomposition(nice_TD, root, directed=True)
sage: label_TD.name()
'Labelled Nice tree decomposition of Tree decomposition'
sage: for node in sorted(label_TD): # random
....: print(node, label_TD.get_vertex(node))
(0, {}) root
(0, {}) forget
(1, {0}) forget
(2, {0, 1}) intro
(3, {0}) forget
(4, {0, 4}) join
(5, {0, 4}) intro
(6, {0, 4}) intro
(7, {0}) forget
(8, {0}) forget
(9, {0, 3}) intro
(10, {0, 2}) intro
(11, {3}) intro
(12, {2}) intro
(13, {}) leaf
(14, {}) leaf
(4, {0, 3}) intro
(5, {0}) forget
(6, {0, 2}) intro
(7, {2}) intro
(8, {}) leaf
"""
from sage.graphs.digraph import DiGraph
from sage.graphs.graph import Graph

directed_TD = DiGraph(nice_TD.breadth_first_search(start=root, edges=True),
format='list_of_edges')
format='list_of_edges',
name='Labelled {}'.format(nice_TD.name()))

# The loop starts from the root node
# We assume the tree decomposition is valid and nice,
# hence saving time on checking.
for node in directed_TD:
in_deg = directed_TD.in_degree(node)
out_deg = directed_TD.out_degree(node)

if in_deg == 0:
directed_TD.set_vertex(node, 'root')
elif out_deg == 2:
if out_deg == 2:
directed_TD.set_vertex(node, 'join')
elif out_deg == 1:
current_bag = node[1]
Expand All @@ -1080,6 +1128,8 @@ def label_nice_tree_decomposition(nice_TD, root):
else:
directed_TD.set_vertex(node, 'leaf')

if directed:
return directed_TD
guojing0 marked this conversation as resolved.
Show resolved Hide resolved
return Graph(directed_TD, name=nice_TD.name())


Expand Down
Loading