From 08939a85105824823ef0c071865d8fc654439560 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 13 Nov 2023 20:12:06 +0100 Subject: [PATCH 1/4] compute the length of a tree decomposition --- .../tree_decomposition.pyx | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx index 90f14298c5a..3a73cbc8cc5 100644 --- a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx @@ -78,6 +78,7 @@ The treewidth of a clique is `n-1` and its treelength is 1:: :meth:`is_valid_tree_decomposition` | Check whether `T` is a valid tree-decomposition for `G`. :meth:`reduced_tree_decomposition` | Return a reduced tree-decomposition of `T`. :meth:`width_of_tree_decomposition` | Return the width of the tree decomposition `T` of `G`. + :meth:`length_of_tree_decomposition` | Return the length of the tree decomposition `T` of `G`. .. TODO: @@ -779,6 +780,116 @@ def treewidth(g, k=None, kmin=None, certificate=False, algorithm=None): # Treelength # +def length_of_tree_decomposition(G, T, check=True): + r""" + Return the length of the tree decomposition `T` of `G`. + + The *length* of a tree decomposition, as proposed in [DG2006]_, 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` (i.e., `\max_{u, v + \in X_i} dist_G(u, v)`). See the documentation of the + :mod:`~sage.graphs.graph_decompositions.tree_decomposition` module for more + details. + + INPUT: + + - ``G`` -- a sage Graph + + - ``T`` -- a tree-decomposition for `G` + + - ``check`` -- boolean (default: ``True``); whether to check that the + tree-decomposition `T` is valid for `G` + + EXAMPLES: + + Trees and cliques have treelength 1:: + + sage: from sage.graphs.graph_decompositions.tree_decomposition import length_of_tree_decomposition + sage: G = graphs.CompleteGraph(5) + sage: tl, T = G.treelength(certificate=True) + sage: tl + 1 + sage: length_of_tree_decomposition(G, T, check=True) + 1 + sage: G = graphs.RandomTree(20) + sage: tl, T = G.treelength(certificate=True) + sage: tl + 1 + sage: length_of_tree_decomposition(G, T, check=True) + 1 + + The Petersen graph has treelength 2:: + + sage: G = graphs.PetersenGraph() + sage: tl, T = G.treelength(certificate=True) + sage: tl + 2 + sage: length_of_tree_decomposition(G, T) + 2 + + When a tree-decomposition has a single bag containing all vertices of a + graph, the length of this tree-decomposition is the diameter of the graph:: + + sage: G = graphs.Grid2dGraph(2, 5) + sage: G.treelength() + 2 + sage: G.diameter() + 5 + sage: T = Graph({Set(G): []}) + sage: length_of_tree_decomposition(G, T) + 5 + + TESTS:: + + sage: G = Graph() + sage: _, T = G.treelength(certificate=True) + sage: length_of_tree_decomposition(G, T, check=True) + 0 + sage: length_of_tree_decomposition(Graph(1), T, check=True) + Traceback (most recent call last): + ... + ValueError: the tree-decomposition is not valid for this graph + """ + if check and not is_valid_tree_decomposition(G, T): + raise ValueError("the tree-decomposition is not valid for this graph") + + cdef unsigned int n = G.order() + + if n < 2: + return 0 + if any(len(bag) == n for bag in T): + return G.diameter() + + cdef unsigned int i, j + + # We map vertices to integers in range 0..n-1 + cdef list int_to_vertex = list(G) + cdef dict vertex_to_int = {u: i for i, u in enumerate(int_to_vertex)} + + # We compute the distance matrix. + cdef unsigned short * c_distances = c_distances_all_pairs(G, vertex_list=int_to_vertex) + cdef unsigned short ** distances = sig_calloc(n, sizeof(unsigned short *)) + for i in range(n): + distances[i] = c_distances + i * n + + # We now compute the maximum lengths of the bags + from itertools import combinations + cdef list bag_int + cdef unsigned short dij + cdef unsigned short length = 0 + for bag in T: + bag_int = [vertex_to_int[u] for u in bag] + for i, j in combinations(bag_int, 2): + dij = distances[i][j] + if dij > length: + length = dij + + sig_free(c_distances) + sig_free(distances) + + return length + + def treelength_lowerbound(G): r""" Return a lower bound on the treelength of `G`. From 3560115cafbbb13b1986b799ea2e12a0cb4bea94 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Thu, 23 Nov 2023 15:04:09 +0100 Subject: [PATCH 2/4] better description of input parameter --- src/sage/graphs/graph_decompositions/tree_decomposition.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx index 3a73cbc8cc5..68049866b15 100644 --- a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx @@ -793,7 +793,7 @@ def length_of_tree_decomposition(G, T, check=True): INPUT: - - ``G`` -- a sage Graph + - ``G`` -- a graph - ``T`` -- a tree-decomposition for `G` From 5b7edf7b5a45a5e8ce7ce4962c445922ca2d6c17 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Thu, 23 Nov 2023 15:28:13 +0100 Subject: [PATCH 3/4] add latex macro \dist and use it --- src/sage/graphs/graph_decompositions/tree_decomposition.pyx | 6 +++--- src/sage/misc/latex_macros.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx index 68049866b15..def8c4e355f 100644 --- a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx @@ -29,7 +29,7 @@ treewidth of a tree equal to one. The *length* of a tree decomposition, as proposed in [DG2006]_, 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` (i.e., `\max_{u, v \in X_i} -dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the minimum length +\dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the minimum length among all possible tree decompositions of `G`. While deciding whether a graph has treelength 1 can be done in linear time @@ -787,7 +787,7 @@ def length_of_tree_decomposition(G, T, check=True): The *length* of a tree decomposition, as proposed in [DG2006]_, 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` (i.e., `\max_{u, v - \in X_i} dist_G(u, v)`). See the documentation of the + \in X_i} \dist_G(u, v)`). See the documentation of the :mod:`~sage.graphs.graph_decompositions.tree_decomposition` module for more details. @@ -1387,7 +1387,7 @@ def treelength(G, k=None, certificate=False): The *length* of a tree decomposition, as proposed in [DG2006]_, 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` (i.e., `\max_{u, v - \in X_i} dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the + \in X_i} \dist_G(u, v)`). The *treelength* `tl(G)` of a graph `G` is the minimum length among all possible tree decompositions of `G`. See the documentation of the :mod:`~sage.graphs.graph_decompositions.tree_decomposition` module for more diff --git a/src/sage/misc/latex_macros.py b/src/sage/misc/latex_macros.py index 238bd02c0e3..93fb11fbf48 100644 --- a/src/sage/misc/latex_macros.py +++ b/src/sage/misc/latex_macros.py @@ -174,7 +174,8 @@ def convert_latex_macro_to_mathjax(macro): # Use this list to define additional latex macros for sage documentation latex_macros = [r"\newcommand{\SL}{\mathrm{SL}}", r"\newcommand{\PSL}{\mathrm{PSL}}", - r"\newcommand{\lcm}{\mathop{\operatorname{lcm}}}"] + r"\newcommand{\lcm}{\mathop{\operatorname{lcm}}}", + r"\newcommand{\dist}{\mathrm{dist}}"] # The following is to allow customization of typesetting of rings: # mathbf vs mathbb. See latex.py for more information. From 7fcc517d44c72771bf609d746b24c1012e54fc4c Mon Sep 17 00:00:00 2001 From: dcoudert Date: Thu, 23 Nov 2023 18:50:52 +0100 Subject: [PATCH 4/4] fix doctest in src/sage/misc/latex.py --- src/sage/misc/latex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 602a24f404a..df469ca8189 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -651,6 +651,7 @@ def latex_extra_preamble(): \newcommand{\SL}{\mathrm{SL}} \newcommand{\PSL}{\mathrm{PSL}} \newcommand{\lcm}{\mathop{\operatorname{lcm}}} + \newcommand{\dist}{\mathrm{dist}} \newcommand{\Bold}[1]{\mathbf{#1}} """