Skip to content

Commit

Permalink
sagemathgh-36846: Resolve nice tree decomp bug in sagemath#36843, and…
Browse files Browse the repository at this point in the history
… allow `label_nice_tree_decomposition` to return a digraph

    
This PR aims to resolve sagemath#36843, i.e., it now handles potential join
nodes and singleton tree decomp correctly.

This PR also allows `label_nice_tree_decomposition` to return a directed
graph, and treats the root node as a `forget` node, simplifying
algorithm implementation.

<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes sagemath#1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->

<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes sagemath#12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- sagemath#12345: short description why this is a dependency
- sagemath#34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: sagemath#36846
Reported by: Jing Guo
Reviewer(s): David Coudert, Dima Pasechnik, Jing Guo
  • Loading branch information
Release Manager committed Dec 12, 2023
2 parents f98a4c5 + fadce49 commit 1abf5c6
Showing 1 changed file with 69 additions and 13 deletions.
82 changes: 69 additions & 13 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
::
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):
r"""
Return a nice tree decomposition with nodes labelled accordingly.
Expand All @@ -1022,6 +1072,10 @@ 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.
Expand All @@ -1033,10 +1087,12 @@ def label_nice_tree_decomposition(nice_TD, root):
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: root = sorted(nice_TD.vertices())[0]
sage: label_TD = label_nice_tree_decomposition(nice_TD, root)
sage: label_TD = label_nice_tree_decomposition(nice_TD, root, directed=True)
sage: print(label_TD.name())
Labelled Nice tree decomposition of Tree decomposition
sage: for node in sorted(label_TD):
....: print(node, label_TD.get_vertex(node))
(0, {}) root
(0, {}) forget
(1, {0}) forget
(2, {0, 1}) intro
(3, {0}) forget
Expand All @@ -1056,18 +1112,16 @@ def label_nice_tree_decomposition(nice_TD, root):
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))

# 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 +1134,8 @@ def label_nice_tree_decomposition(nice_TD, root):
else:
directed_TD.set_vertex(node, 'leaf')

if directed:
return directed_TD
return Graph(directed_TD, name=nice_TD.name())


Expand Down

0 comments on commit 1abf5c6

Please sign in to comment.