Skip to content

Commit d628002

Browse files
author
Release Manager
committed
gh-36705: add method to compute the length of a tree-decomposition Given a graph `G` and a tree-decomposition `T` of `G`, the _length_ of the tree-decomposition `T` is the maximum _diameter_ in `G` of its bags, where the diameter of a bag `X_i` is the largest distance in `G` between the vertices in `X_i`. We add a method to compute the _length_ of a given tree-decomposition of a graph. ### 📝 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. - [ ] 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: #36705 Reported by: David Coudert Reviewer(s): David Coudert, Kwankyu Lee
2 parents deb867c + 7fcc517 commit d628002

File tree

3 files changed

+116
-3
lines changed

3 files changed

+116
-3
lines changed

src/sage/graphs/graph_decompositions/tree_decomposition.pyx

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ treewidth of a tree equal to one.
2929
The *length* of a tree decomposition, as proposed in [DG2006]_, is the maximum
3030
*diameter* in `G` of its bags, where the diameter of a bag `X_i` is the largest
3131
distance in `G` between the vertices in `X_i` (i.e., `\max_{u, v \in X_i}
32-
dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the minimum length
32+
\dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the minimum length
3333
among all possible tree decompositions of `G`.
3434
3535
While deciding whether a graph has treelength 1 can be done in linear time
@@ -80,6 +80,7 @@ The treewidth of a clique is `n-1` and its treelength is 1::
8080
:meth:`is_valid_tree_decomposition` | Check whether `T` is a valid tree-decomposition for `G`.
8181
:meth:`reduced_tree_decomposition` | Return a reduced tree-decomposition of `T`.
8282
:meth:`width_of_tree_decomposition` | Return the width of the tree decomposition `T` of `G`.
83+
:meth:`length_of_tree_decomposition` | Return the length of the tree decomposition `T` of `G`.
8384
8485
8586
.. TODO:
@@ -1086,6 +1087,116 @@ def label_nice_tree_decomposition(nice_TD, root):
10861087
# Treelength
10871088
#
10881089

1090+
def length_of_tree_decomposition(G, T, check=True):
1091+
r"""
1092+
Return the length of the tree decomposition `T` of `G`.
1093+
1094+
The *length* of a tree decomposition, as proposed in [DG2006]_, is the
1095+
maximum *diameter* in `G` of its bags, where the diameter of a bag `X_i` is
1096+
the largest distance in `G` between the vertices in `X_i` (i.e., `\max_{u, v
1097+
\in X_i} \dist_G(u, v)`). See the documentation of the
1098+
:mod:`~sage.graphs.graph_decompositions.tree_decomposition` module for more
1099+
details.
1100+
1101+
INPUT:
1102+
1103+
- ``G`` -- a graph
1104+
1105+
- ``T`` -- a tree-decomposition for `G`
1106+
1107+
- ``check`` -- boolean (default: ``True``); whether to check that the
1108+
tree-decomposition `T` is valid for `G`
1109+
1110+
EXAMPLES:
1111+
1112+
Trees and cliques have treelength 1::
1113+
1114+
sage: from sage.graphs.graph_decompositions.tree_decomposition import length_of_tree_decomposition
1115+
sage: G = graphs.CompleteGraph(5)
1116+
sage: tl, T = G.treelength(certificate=True)
1117+
sage: tl
1118+
1
1119+
sage: length_of_tree_decomposition(G, T, check=True)
1120+
1
1121+
sage: G = graphs.RandomTree(20)
1122+
sage: tl, T = G.treelength(certificate=True)
1123+
sage: tl
1124+
1
1125+
sage: length_of_tree_decomposition(G, T, check=True)
1126+
1
1127+
1128+
The Petersen graph has treelength 2::
1129+
1130+
sage: G = graphs.PetersenGraph()
1131+
sage: tl, T = G.treelength(certificate=True)
1132+
sage: tl
1133+
2
1134+
sage: length_of_tree_decomposition(G, T)
1135+
2
1136+
1137+
When a tree-decomposition has a single bag containing all vertices of a
1138+
graph, the length of this tree-decomposition is the diameter of the graph::
1139+
1140+
sage: G = graphs.Grid2dGraph(2, 5)
1141+
sage: G.treelength()
1142+
2
1143+
sage: G.diameter()
1144+
5
1145+
sage: T = Graph({Set(G): []})
1146+
sage: length_of_tree_decomposition(G, T)
1147+
5
1148+
1149+
TESTS::
1150+
1151+
sage: G = Graph()
1152+
sage: _, T = G.treelength(certificate=True)
1153+
sage: length_of_tree_decomposition(G, T, check=True)
1154+
0
1155+
sage: length_of_tree_decomposition(Graph(1), T, check=True)
1156+
Traceback (most recent call last):
1157+
...
1158+
ValueError: the tree-decomposition is not valid for this graph
1159+
"""
1160+
if check and not is_valid_tree_decomposition(G, T):
1161+
raise ValueError("the tree-decomposition is not valid for this graph")
1162+
1163+
cdef unsigned int n = G.order()
1164+
1165+
if n < 2:
1166+
return 0
1167+
if any(len(bag) == n for bag in T):
1168+
return G.diameter()
1169+
1170+
cdef unsigned int i, j
1171+
1172+
# We map vertices to integers in range 0..n-1
1173+
cdef list int_to_vertex = list(G)
1174+
cdef dict vertex_to_int = {u: i for i, u in enumerate(int_to_vertex)}
1175+
1176+
# We compute the distance matrix.
1177+
cdef unsigned short * c_distances = c_distances_all_pairs(G, vertex_list=int_to_vertex)
1178+
cdef unsigned short ** distances = <unsigned short **>sig_calloc(n, sizeof(unsigned short *))
1179+
for i in range(n):
1180+
distances[i] = c_distances + i * n
1181+
1182+
# We now compute the maximum lengths of the bags
1183+
from itertools import combinations
1184+
cdef list bag_int
1185+
cdef unsigned short dij
1186+
cdef unsigned short length = 0
1187+
for bag in T:
1188+
bag_int = [vertex_to_int[u] for u in bag]
1189+
for i, j in combinations(bag_int, 2):
1190+
dij = distances[i][j]
1191+
if dij > length:
1192+
length = dij
1193+
1194+
sig_free(c_distances)
1195+
sig_free(distances)
1196+
1197+
return length
1198+
1199+
10891200
def treelength_lowerbound(G):
10901201
r"""
10911202
Return a lower bound on the treelength of `G`.
@@ -1583,7 +1694,7 @@ def treelength(G, k=None, certificate=False):
15831694
The *length* of a tree decomposition, as proposed in [DG2006]_, is the
15841695
maximum *diameter* in `G` of its bags, where the diameter of a bag `X_i` is
15851696
the largest distance in `G` between the vertices in `X_i` (i.e., `\max_{u, v
1586-
\in X_i} dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the
1697+
\in X_i} \dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the
15871698
minimum length among all possible tree decompositions of `G`.
15881699
See the documentation of the
15891700
:mod:`~sage.graphs.graph_decompositions.tree_decomposition` module for more

src/sage/misc/latex.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ def latex_extra_preamble():
651651
\newcommand{\SL}{\mathrm{SL}}
652652
\newcommand{\PSL}{\mathrm{PSL}}
653653
\newcommand{\lcm}{\mathop{\operatorname{lcm}}}
654+
\newcommand{\dist}{\mathrm{dist}}
654655
\newcommand{\Bold}[1]{\mathbf{#1}}
655656
<BLANKLINE>
656657
"""

src/sage/misc/latex_macros.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ def convert_latex_macro_to_mathjax(macro):
174174
# Use this list to define additional latex macros for sage documentation
175175
latex_macros = [r"\newcommand{\SL}{\mathrm{SL}}",
176176
r"\newcommand{\PSL}{\mathrm{PSL}}",
177-
r"\newcommand{\lcm}{\mathop{\operatorname{lcm}}}"]
177+
r"\newcommand{\lcm}{\mathop{\operatorname{lcm}}}",
178+
r"\newcommand{\dist}{\mathrm{dist}}"]
178179

179180
# The following is to allow customization of typesetting of rings:
180181
# mathbf vs mathbb. See latex.py for more information.

0 commit comments

Comments
 (0)