Skip to content

Commit 1abf5c6

Browse files
author
Release Manager
committed
gh-36846: Resolve nice tree decomp bug in #36843, and allow `label_nice_tree_decomposition` to return a digraph This PR aims to resolve #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 #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 #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 - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #36846 Reported by: Jing Guo Reviewer(s): David Coudert, Dima Pasechnik, Jing Guo
2 parents f98a4c5 + fadce49 commit 1abf5c6

File tree

1 file changed

+69
-13
lines changed

1 file changed

+69
-13
lines changed

src/sage/graphs/graph_decompositions/tree_decomposition.pyx

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ def make_nice_tree_decomposition(graph, tree_decomp):
818818
INPUT:
819819
820820
- ``graph`` -- a Sage graph
821+
821822
- ``tree_decomp`` -- a tree decomposition
822823
823824
OUTPUT:
@@ -853,6 +854,46 @@ def make_nice_tree_decomposition(graph, tree_decomp):
853854
sage: bip_one_four_TD = bip_one_four.treewidth(certificate=True)
854855
sage: make_nice_tree_decomposition(bip_one_four, bip_one_four_TD)
855856
Nice tree decomposition of Tree decomposition: Graph on 15 vertices
857+
858+
Check that :issue:`36843` is fixed::
859+
860+
sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
861+
sage: triangle = graphs.CompleteGraph(3)
862+
sage: triangle_TD = triangle.treewidth(certificate=True)
863+
sage: make_nice_tree_decomposition(triangle, triangle_TD)
864+
Nice tree decomposition of Tree decomposition: Graph on 7 vertices
865+
866+
::
867+
868+
sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
869+
sage: graph = graphs.CompleteBipartiteGraph(2, 5)
870+
sage: graph_TD = graph.treewidth(certificate=True)
871+
sage: make_nice_tree_decomposition(graph, graph_TD)
872+
Nice tree decomposition of Tree decomposition: Graph on 25 vertices
873+
874+
::
875+
876+
sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
877+
sage: empty_graph = graphs.EmptyGraph()
878+
sage: tree_decomp = empty_graph.treewidth(certificate=True)
879+
sage: len(make_nice_tree_decomposition(empty_graph, tree_decomp))
880+
0
881+
882+
::
883+
884+
sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
885+
sage: singleton = graphs.CompleteGraph(1)
886+
sage: tree_decomp = singleton.treewidth(certificate=True)
887+
sage: make_nice_tree_decomposition(singleton, tree_decomp)
888+
Nice tree decomposition of Tree decomposition: Graph on 3 vertices
889+
890+
::
891+
892+
sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition
893+
sage: an_edge = graphs.CompleteGraph(2)
894+
sage: tree_decomp = an_edge.treewidth(certificate=True)
895+
sage: make_nice_tree_decomposition(an_edge, tree_decomp)
896+
Nice tree decomposition of Tree decomposition: Graph on 5 vertices
856897
"""
857898
if not is_valid_tree_decomposition(graph, tree_decomp):
858899
raise ValueError("input must be a valid tree decomposition for this graph")
@@ -864,11 +905,19 @@ def make_nice_tree_decomposition(graph, tree_decomp):
864905

865906
# Step 1: Ensure the tree is directed and has a root
866907
# Choose a root and orient the edges from root-to-leaves direction
867-
leaves = [u for u in tree_decomp if tree_decomp.degree(u) == 1]
868-
root = leaves.pop()
908+
#
909+
# Testing <= 1 for the special case when one bag containing all vertices
910+
leaves = [u for u in tree_decomp if tree_decomp.degree(u) <= 1]
911+
869912
from sage.graphs.digraph import DiGraph
870-
directed_tree = DiGraph(tree_decomp.breadth_first_search(start=root, edges=True),
871-
format='list_of_edges')
913+
if len(leaves) == 1:
914+
root = leaves[0]
915+
directed_tree = DiGraph(tree_decomp)
916+
else:
917+
root = leaves.pop()
918+
919+
directed_tree = DiGraph(tree_decomp.breadth_first_search(start=root, edges=True),
920+
format='list_of_edges')
872921

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

908-
directed_tree.delete_edges((ui, vi) for v in children)
957+
directed_tree.delete_edges((ui, vi) for vi in children)
909958

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

@@ -1012,7 +1061,8 @@ def make_nice_tree_decomposition(graph, tree_decomp):
10121061

10131062
return nice_tree_decomp
10141063

1015-
def label_nice_tree_decomposition(nice_TD, root):
1064+
1065+
def label_nice_tree_decomposition(nice_TD, root, directed=False):
10161066
r"""
10171067
Return a nice tree decomposition with nodes labelled accordingly.
10181068
@@ -1022,6 +1072,10 @@ def label_nice_tree_decomposition(nice_TD, root):
10221072
10231073
- ``root`` -- the root of the nice tree decomposition
10241074
1075+
- ``directed`` -- boolean (default: ``False``); whether to return the nice
1076+
tree decomposition as a directed graph rooted at vertex ``root`` or as an
1077+
undirected graph
1078+
10251079
OUTPUT:
10261080
10271081
A nice tree decomposition with nodes labelled.
@@ -1033,10 +1087,12 @@ def label_nice_tree_decomposition(nice_TD, root):
10331087
sage: bip_one_four_TD = bip_one_four.treewidth(certificate=True)
10341088
sage: nice_TD = make_nice_tree_decomposition(bip_one_four, bip_one_four_TD)
10351089
sage: root = sorted(nice_TD.vertices())[0]
1036-
sage: label_TD = label_nice_tree_decomposition(nice_TD, root)
1090+
sage: label_TD = label_nice_tree_decomposition(nice_TD, root, directed=True)
1091+
sage: print(label_TD.name())
1092+
Labelled Nice tree decomposition of Tree decomposition
10371093
sage: for node in sorted(label_TD):
10381094
....: print(node, label_TD.get_vertex(node))
1039-
(0, {}) root
1095+
(0, {}) forget
10401096
(1, {0}) forget
10411097
(2, {0, 1}) intro
10421098
(3, {0}) forget
@@ -1056,18 +1112,16 @@ def label_nice_tree_decomposition(nice_TD, root):
10561112
from sage.graphs.graph import Graph
10571113

10581114
directed_TD = DiGraph(nice_TD.breadth_first_search(start=root, edges=True),
1059-
format='list_of_edges')
1115+
format='list_of_edges',
1116+
name='Labelled {}'.format(nice_TD))
10601117

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

1068-
if in_deg == 0:
1069-
directed_TD.set_vertex(node, 'root')
1070-
elif out_deg == 2:
1124+
if out_deg == 2:
10711125
directed_TD.set_vertex(node, 'join')
10721126
elif out_deg == 1:
10731127
current_bag = node[1]
@@ -1080,6 +1134,8 @@ def label_nice_tree_decomposition(nice_TD, root):
10801134
else:
10811135
directed_TD.set_vertex(node, 'leaf')
10821136

1137+
if directed:
1138+
return directed_TD
10831139
return Graph(directed_TD, name=nice_TD.name())
10841140

10851141

0 commit comments

Comments
 (0)