From f98bc6e48426cca78812c169af091554ed5433e5 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Fri, 10 May 2024 16:57:54 +0200 Subject: [PATCH 1/7] cographs generator --- src/doc/en/reference/graphs/index.rst | 1 + src/doc/en/reference/references/index.rst | 5 + src/sage/graphs/cographs.py | 571 ++++++++++++++++++++++ src/sage/graphs/graph_generators.py | 3 + 4 files changed, 580 insertions(+) create mode 100644 src/sage/graphs/cographs.py diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index 496f00ebcba..f681c083a08 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -78,6 +78,7 @@ Libraries of algorithms sage/graphs/centrality sage/graphs/asteroidal_triples sage/graphs/independent_sets + sage/graphs/cographs sage/graphs/comparability sage/graphs/line_graph sage/graphs/spanning_tree diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 894b680b2a9..556a3ed9c5d 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3624,6 +3624,11 @@ REFERENCES: .. [Jon2005] \V. Jones, The Jones Polynomial, 2005. https://math.berkeley.edu/~vfr/jones.pdf +.. [JPD2018] Átila A. Jones, Fábio Protti and Renata R. Del-Vecchio: + *Cograph generation with linear delay*. + Theoretical Computer Science, 713:1-10, 2018. + :doi:`10.1016/j.tcs.2017.12.037` + .. [JRJ94] Jourdan, Guy-Vincent; Rampon, Jean-Xavier; Jard, Claude (1994), "Computing on-line the lattice of maximal antichains of posets", Order 11 (3) p. 197-210, :doi:`10.1007/BF02115811` diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py new file mode 100644 index 00000000000..516b9fb89b5 --- /dev/null +++ b/src/sage/graphs/cographs.py @@ -0,0 +1,571 @@ +r""" +Cographs + +A cograph is a `P_4`-free graph, that is a graph without induced path of order +4. Any cograph may be constructed, starting from the single vertex graph, by a +sequence of join and disjoint union operations. See the :wikipedia:`Cograph` for +more details on this graph class, and :oeis:`A000084` to know the number of +cographs of order `n \geq 1`. + +This module implements the folowing methods concerning cographs: + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + + :meth:`cographs` | Return an iterator over the cographs of order `n`. + +Methods +------- +""" +# **************************************************************************** +# Copyright (C) 2024 David Coudert +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + + +class CoTree: + """ + Generic cotree node. + + This data structure is used for the generation of cographs in + :meth:`cographs`. + """ + def __init__(self, name='root'): + r""" + Initialize a cotree. + + INPUT: + + - ``name`` -- either an operation ('U' or 'J') or the size of the + subtree rooted at this node + + EXAMPLES:: + + sage: from sage.graphs.cographs import CoTree + sage: CoTree(1) + ( 1 ) + sage: CoTree() + ( root ) + """ + self.name = name + self.children = [] + self.info = None + self.parent = None + + def __repr__(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.graphs.cographs import CoTree + sage: CoTree(1) + ( 1 ) + sage: CoTree('J') + [ J ] + sage: next(graphs.cographs(4, as_graph=False)) # indirect doctest + [ J ( 0 ) ( 1 ) ( 2 ) ( 3 ) ] + """ + return self.__str__() + + def __str__(self): + r""" + Return a string representation of ``self``. + + The subtree of a node is inside brackets and starts with the operation + to perform between the children ('J' for join or 'U' for disjoint + union). + + EXAMPLES:: + + sage: from sage.graphs.cographs import CoTree + sage: CoTree(1) + ( 1 ) + sage: CoTree('J') + [ J ] + sage: next(graphs.cographs(4, as_graph=False)) # indirect doctest + [ J ( 0 ) ( 1 ) ( 2 ) ( 3 ) ] + """ + first = '[' if self.name in ['J', 'U'] else '(' + last = ' ]' if self.name in ['J', 'U'] else ' )' + s = f"{first} {self.name}" + for child in self.children: + s += f' {child}' + s += last + return s + + + def add_child(self, node): + r""" + Add cotree ``node`` in the list of children of ``self``. + + INPUT: + + - ``node`` -- a CoTree + + EXAMPLES:: + + sage: from sage.graphs.cographs import CoTree + sage: T = CoTree('J') + sage: T.add_child(CoTree(1)) + sage: T + [ J ( 1 ) ] + """ + assert isinstance(node, CoTree) + self.children.append(node) + node.parent = self + + def copy_tree(self, T): + r""" + Make `T` a copy of ``self``. + + INPUT: + + - ``T`` -- a CoTree + + EXAMPLES:: + + sage: from sage.graphs.cographs import CoTree + sage: T = CoTree('J') + sage: T.add_child(CoTree(1)) + sage: T.add_child(CoTree(2)) + sage: T + [ J ( 1 ) ( 2 ) ] + sage: B = CoTree('U') + sage: T.copy_tree(B) + sage: B + [ U ( 1 ) ( 2 ) ] + """ + for i, child in enumerate(self.children): + T.add_child(CoTree(child.name)) + child.copy_tree(T.children[i]) + + def reset_info(self): + r""" + Reset parameter ``info`` from all nodes of ``self``. + + EXAMPLES:: + + sage: from sage.graphs.cographs import CoTree + sage: T = CoTree(1) + sage: B = CoTree(2) + sage: T.add_child(B) + sage: C = CoTree(3) + sage: B.add_child(C) + sage: C.info = 'info' + sage: T.reset_info() + sage: C.info is None + True + """ + for child in self.children: + child.reset_info() + self.info = None + + +def next_partition(P): + r""" + Return the next partition after `P`, if any. + + This is a helper method to method :meth:`cographs`. + + INPUT: + + - ``P`` -- a list encoding a partition of a number `n` + + EXAMPLES:: + + sage: from sage.graphs.cographs import next_partition + sage: P = [1, 1, 1, 1, 1, 1] + sage: while P: + ....: print(P) + ....: P = next_partition(P) + [1, 1, 1, 1, 1, 1] + [1, 1, 1, 1, 2] + [1, 1, 1, 3] + [1, 1, 2, 2] + [1, 1, 4] + [1, 2, 3] + [1, 5] + [2, 2, 2] + [2, 4] + [3, 3] + """ + if len(P) < 2: + raise ValueError("the length of the input partition must be at least 2") + n = sum(P) + if P[0] != n//2: + if P[-1] - P[-2] <= 1: + return P[:-2] + [P[-2] + P[-1]] + + x = P[-2] + 1 + y = P[-1] - 1 + q = y // x + if q > 1: + r = y % x + return P[:-2] + [x]*q + [x + r] + return P[:-2] + [x, y] + + if n == 3 and P[1] != n//2 + n%2: + return [1, 2] + return None + + +def rebuild_node(u, P): + r""" + Replace the subtree rooted at `u` by a subtree induced by partition `P`. + + This is a helper method to method :meth:`cographs`. + + INPUT: + + - ``u`` -- a ``CoTree`` + + - ``P`` -- a partition encoding the new structure of the children of `u` + + EXAMPLES:: + + sage: next(graphs.cographs(3, as_graph=True)).vertices() # indirect doctest + [0, 1, 2] + """ + u.children = [] # delete the subtree rooted at u + for value in P: + this_child = CoTree(value) + u.add_child(this_child) + if value > 1: + for j in range(value): + this_child.add_child(CoTree(1)) + + +def find_pivot(T): + r""" + Seach for a pivot node in `T`. + + This is a helper method to method :meth:`cographs`. + + A node in `T` is a ``pivot`` if it is not a leaf, it does not induce a + maximum partition and it is the first such node in the inverse postorder + traversal. + + INPUT: + + - ``T`` -- a ``CoTree`` + + EXAMPLES:: + + sage: next(graphs.cographs(3, as_graph=True)).vertices() # indirect doctest + [0, 1, 2] + """ + for child in reversed(T.children): + pivot = find_pivot(child) + if pivot is not None: + return pivot + + # Check if T is a pivot + i = T.name + if (i != 1 and ((i//2 != T.children[0].name) or + (i//2 + i%2 != T.children[1].name))): + T.info = 'p' # pivot mark + return T + return None + + +def next_tree(T): + r""" + Check if there is another tree after `T`. + + This is a helper method to method :meth:`cographs`. + + This methods returns ``True`` if there is a tree after `T`, and if so it + modifies the input tree `T` that becomes the next tree. + + INPUT: + + - ``T`` -- a ``CoTree`` + + EXAMPLES:: + + sage: next(graphs.cographs(3, as_graph=True)).vertices() # indirect doctest + [0, 1, 2] + """ + pivot = find_pivot(T) + if pivot is None: + return False + + # Find the next partition induced by the subtree pivot + partition = [c.name for c in pivot.children] + P = next_partition(partition) + # and rebuild the subtree of pivot accordingly + rebuild_node(pivot, P) + + x = pivot + while x.parent is not None: + ancestor = x.parent + # Modify the bigger siblings of x (i.e., the siblings placed after + # x in the list of children of ancestor) + is_bigger_sibling = False + for y, this_child in enumerate(ancestor.children): + if this_child.info == 'p': + is_bigger_sibling = True + elif is_bigger_sibling: # true only for bigger siblings of x + if x.name == this_child.name: + temp = CoTree(x.name) + x.copy_tree(temp) + ancestor.children[y] = temp # copy subtree T(x) in T(y) + temp.parent = ancestor + else: + P = [1] * ancestor.children[y].name + rebuild_node(ancestor.children[y], P) + + # Make the parent of x (if any) the new pivot + x.info = None # reset the pivot mark + ancestor.info = 'p' # parent gets the pivot mark + x = ancestor + + return True + + +def cographs(n, as_graph=True, immutable=False): + r""" + Return an iterator over the cographs of order `n`. + + A cograph is a `P_4`-free graph, that is a graph without induced path of + order 4. Any cograph may be constructed, starting from the single vertex + graph, by a sequence of :meth:`sage.graphs.graph.Graph.join` and + :meth:`sage.graphs.generic_graph.GenericGraph.disjoint_union` operations. + See the :wikipedia:`Cograph` for more details. + + This method implements the generator of all cographs of order `n` proposd + in [JPD2018]_. The algorithm generates one by one every cotree with `n` nodes, + and each cotree is generated by using its previous cotree. The time to + construct the first cotree is `O(n)` and the time spent between two + consecutive outputs is `O(n)`. Hence, the overall complexity of the algorithm + is `O(n*Mn)`, where `n` is the number of nodes and `Mn` is the total number of + cographs with `n` nodes (see :oeis:`A000084`). + + INPUT: + + - ``n`` -- an integer larger or equal to 1 + + - ``as_graph`` -- boolean (default: ``True``); whether to return graphs or + the tree data structure encoding the graph + + - ``immutable`` -- boolean (default: ``False``); whether to return an + immutable or a mutable graph. This parameter is used only when ``as_graph + is True``. + + EXAMPLES: + + The output can be either cotrees or graphs:: + + sage: for t in graphs.cographs(3, as_graph=False): + ....: print(t) + [ J ( 0 ) ( 1 ) ( 2 ) ] + [ J [ U ( 0 ) ( 1 ) ( 2 ) ] ] + [ J ( 0 ) [ U ( 1 ) ( 2 ) ] ] + [ J [ U ( 0 ) [ J ( 1 ) ( 2 ) ] ] ] + sage: for g in graphs.cographs(3, as_graph=True): + ....: print(g.edges(labels=False, sort=True)) + [(0, 1), (0, 2), (1, 2)] + [] + [(0, 1), (0, 2)] + [(1, 2)] + + Check that we agree with :oeis:`A000084`:: + + sage: [sum(1 for _ in graphs.cographs(n, as_graph=False)) + ....: for n in range(1, 8)] + [1, 2, 4, 10, 24, 66, 180] + + TESTS:: + + sage: g = next(graphs.cographs(2, as_graph=True, immutable=False)) + sage: g.is_immutable() + False + sage: g = next(graphs.cographs(2, as_graph=True, immutable=True)) + sage: g.is_immutable() + True + sage: next(graphs.cographs(0)) + Traceback (most recent call last): + ... + ValueError: parameter n must be at least >= 1 + """ + if n < 1: + raise ValueError('parameter n must be at least >= 1') + if as_graph: + def func(T): + return tree_to_graph(T, immutable=immutable) + else: + def func(T): + return T + + if n == 1: + T = CoTree('J') + B = CoTree('U') + B.add_child(CoTree(0)) + T.add_child(B) + yield func(T) + return + + # Construct the minimum tree + T = CoTree(n) + for j in range(n): + T.add_child(CoTree(1)) + + while True: + # T corresponds to 2 cotrees: one with 'U' root and one with 'J' root + tree_J = CoTree(T.name) + T.copy_tree(tree_J) + tree_U = CoTree(T.name) + T.copy_tree(tree_U) + # tree_J has root 'J' + change_label(tree_J, True, [0]) + # tree 0 has root 'U' + change_label(tree_U, False, [0]) + tree_UU = CoTree('J') + tree_UU.add_child(tree_U) + yield func(tree_J) + yield func(tree_UU) + if not next_tree(T): + break + + +def change_label(tree, status, counter): + """ + Set the names of the nodes of ``tree``. + + This is a helper method to method :meth:`cographs`. + + The input ``tree`` is such that each node has as label its number of + children. This method changes the label of each node so that a parallel + node is labeled 'U', a series node is labeled 'J' and a leaf node gets a + unique number. + + INPUT: + + - ``tree`` -- the tree to relabel + + - ``status`` -- boolean; used to to detect series (``True``) and parallel + (``False``) internal nodes + + - ``counter`` -- list; the first integer of the list is used to assign a + unique number to the leaves of the tree + + EXAMPLES:: + + sage: next(graphs.cographs(4, as_graph=True)).vertices() # indirect doctest + [0, 1, 2, 3] + """ + if tree.name != 1: + tree.name = 'J' if status else 'U' + else: + tree.name = counter[0] + counter[0] += 1 + for child in tree.children: + if child is not None: + change_label(child, not status, counter) + + +def tree_to_graph(tree, immutable=False): + r""" + Return the cograph represented by ``tree``. + + This is a helper method to method :meth:`cographs`. + + EXAMPLES:: + + sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest + ....: print(t.edges(labels=False, sort=True)) # indirect doctest + [(0, 1), (0, 2), (1, 2)] + [] + [(0, 1), (0, 2)] + [(1, 2)] + """ + from sage.graphs.graph import Graph + g = Graph() + _tree_to_graph_rec(tree, g) + return g.copy(immutable=True) if immutable else g + + +def _tree_to_graph_rec(tree, g): + r""" + Add recursively one by one the vertices and edges of ``tree`` to ``g``. + + This is a helper method to method :meth:`tree_to_graph`. + + EXAMPLES:: + + sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest + ....: print(t.edges(labels=False, sort=True)) # indirect doctest + [(0, 1), (0, 2), (1, 2)] + [] + [(0, 1), (0, 2)] + [(1, 2)] + """ + for child in tree.children: + _tree_to_graph_rec(child, g) + if tree.name not in ['J', 'U']: + tree.info = 'v' + g.add_vertex(tree.name) + _find_neighbors(tree, g) + tree.reset_info() + + +def _find_neighbors(tree, g): + r""" + Identify the neighbors of node ``tree`` in ``g`` and add needed edges. + + This is a helper method to method :meth:`tree_to_graph`. + + EXAMPLES:: + + sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest + ....: print(t.edges(labels=False, sort=True)) # indirect doctest + [(0, 1), (0, 2), (1, 2)] + [] + [(0, 1), (0, 2)] + [(1, 2)] + """ + ancestor = tree.parent + ancestor.info = 'v' + while ancestor is not None: + if ancestor.name == 'J': + for sibling in ancestor.children: + if sibling != tree and sibling.info != 'v': + _add_edge(tree, sibling, g) + elif ancestor.name == 'U': + ancestor.info = 'v' + ancestor = ancestor.parent + + +def _add_edge(u, v, g): + r""" + Add an edge in `g` between the nodes associated to the cotrees `u` and `v`. + + This is a helper method to method :meth:`tree_to_graph`. + + If `v` is not a leaf, then the method add edges between `u` and all leaves + of the subtree rooted at `v`. + + The method assumes that `u` is a leaf (not tested). + + EXAMPLES:: + + sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest + ....: print(t.edges(labels=False, sort=True)) # indirect doctest + [(0, 1), (0, 2), (1, 2)] + [] + [(0, 1), (0, 2)] + [(1, 2)] + """ + if v.name in ['J', 'U']: + for child in v.children: + _add_edge(u, child, g) + else: + g.add_edge(u.name, v.name) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 5a09c617ba2..d7b5af8fc22 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -232,6 +232,7 @@ def wrap_name(x): "CaiFurerImmermanGraph", "chang_graphs", "CirculantGraph", + "cographs", "cospectral_graphs", "CubeGraph", "CubeConnectedCycle", @@ -2632,6 +2633,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None ########################################################################### # Families ########################################################################### + from . import cographs as cographs_module from .generators import families from . import strongly_regular_db AlternatingFormsGraph = staticmethod(distance_regular.AlternatingFormsGraph) @@ -2643,6 +2645,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None CaiFurerImmermanGraph = staticmethod(families.CaiFurerImmermanGraph) chang_graphs = staticmethod(families.chang_graphs) CirculantGraph = staticmethod(families.CirculantGraph) + cographs = staticmethod(cographs_module.cographs) CubeGraph = staticmethod(families.CubeGraph) CubeConnectedCycle = staticmethod(families.CubeConnectedCycle) DipoleGraph = staticmethod(families.DipoleGraph) From 6323dab97388d09b0957e72482c9a6eeaf4ff058 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Fri, 10 May 2024 17:14:31 +0200 Subject: [PATCH 2/7] fix lint --- src/sage/graphs/cographs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index 516b9fb89b5..e816166d6dc 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -481,7 +481,7 @@ def tree_to_graph(tree, immutable=False): EXAMPLES:: sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest - ....: print(t.edges(labels=False, sort=True)) # indirect doctest + ....: print(t.edges(labels=False, sort=True)) [(0, 1), (0, 2), (1, 2)] [] [(0, 1), (0, 2)] @@ -502,7 +502,7 @@ def _tree_to_graph_rec(tree, g): EXAMPLES:: sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest - ....: print(t.edges(labels=False, sort=True)) # indirect doctest + ....: print(t.edges(labels=False, sort=True)) [(0, 1), (0, 2), (1, 2)] [] [(0, 1), (0, 2)] @@ -526,7 +526,7 @@ def _find_neighbors(tree, g): EXAMPLES:: sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest - ....: print(t.edges(labels=False, sort=True)) # indirect doctest + ....: print(t.edges(labels=False, sort=True)) [(0, 1), (0, 2), (1, 2)] [] [(0, 1), (0, 2)] @@ -558,7 +558,7 @@ def _add_edge(u, v, g): EXAMPLES:: sage: for t in graphs.cographs(3, as_graph=True): # indirect doctest - ....: print(t.edges(labels=False, sort=True)) # indirect doctest + ....: print(t.edges(labels=False, sort=True)) [(0, 1), (0, 2), (1, 2)] [] [(0, 1), (0, 2)] From 8b603483b9f40d300bb4432b8e4b848e6e34c9d5 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Fri, 10 May 2024 17:26:20 +0200 Subject: [PATCH 3/7] more lint issues --- src/sage/graphs/cographs.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index e816166d6dc..67fb3d72c36 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -101,7 +101,6 @@ def __str__(self): s += last return s - def add_child(self, node): r""" Add cotree ``node`` in the list of children of ``self``. @@ -198,7 +197,7 @@ def next_partition(P): [3, 3] """ if len(P) < 2: - raise ValueError("the length of the input partition must be at least 2") + raise ValueError("the length of the input partition must be at least 2") n = sum(P) if P[0] != n//2: if P[-1] - P[-2] <= 1: @@ -212,7 +211,7 @@ def next_partition(P): return P[:-2] + [x]*q + [x + r] return P[:-2] + [x, y] - if n == 3 and P[1] != n//2 + n%2: + if n == 3 and P[1] != n//2 + n % 2: return [1, 2] return None @@ -270,7 +269,7 @@ def find_pivot(T): # Check if T is a pivot i = T.name if (i != 1 and ((i//2 != T.children[0].name) or - (i//2 + i%2 != T.children[1].name))): + (i//2 + i % 2 != T.children[1].name))): T.info = 'p' # pivot mark return T return None @@ -314,7 +313,7 @@ def next_tree(T): if this_child.info == 'p': is_bigger_sibling = True elif is_bigger_sibling: # true only for bigger siblings of x - if x.name == this_child.name: + if x.name == this_child.name: temp = CoTree(x.name) x.copy_tree(temp) ancestor.children[y] = temp # copy subtree T(x) in T(y) From a70f6c2cbdcbe34f501ef69d1bcb360093ac04b3 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 12 May 2024 14:29:50 +0200 Subject: [PATCH 4/7] fix typos --- src/sage/graphs/cographs.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index 67fb3d72c36..a85ba748a6a 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -31,7 +31,7 @@ class CoTree: - """ + r""" Generic cotree node. This data structure is used for the generation of cographs in @@ -340,13 +340,13 @@ def cographs(n, as_graph=True, immutable=False): :meth:`sage.graphs.generic_graph.GenericGraph.disjoint_union` operations. See the :wikipedia:`Cograph` for more details. - This method implements the generator of all cographs of order `n` proposd - in [JPD2018]_. The algorithm generates one by one every cotree with `n` nodes, - and each cotree is generated by using its previous cotree. The time to - construct the first cotree is `O(n)` and the time spent between two - consecutive outputs is `O(n)`. Hence, the overall complexity of the algorithm - is `O(n*Mn)`, where `n` is the number of nodes and `Mn` is the total number of - cographs with `n` nodes (see :oeis:`A000084`). + This method implements the generator of all cographs of order `n` proposed + in [JPD2018]_. The algorithm generates one by one every cotree with `n` + nodes, and each cotree is generated by using its previous cotree. The time + to construct the first cotree is `O(n)` and the time spent between two + consecutive outputs is `O(n)`. Hence, the overall complexity of the + algorithm is `O(n*M_n)`, where `n` is the number of nodes and `M_n` is the + total number of cographs with `n` nodes (see :oeis:`A000084`). INPUT: @@ -436,7 +436,7 @@ def func(T): def change_label(tree, status, counter): - """ + r""" Set the names of the nodes of ``tree``. This is a helper method to method :meth:`cographs`. From a1b3a44fcc55bed6a9c3718f9382fd165a09984a Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 3 Jun 2024 11:58:46 +0200 Subject: [PATCH 5/7] use AccelAsc_next --- src/sage/graphs/cographs.py | 63 +++++++------------------------------ 1 file changed, 11 insertions(+), 52 deletions(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index a85ba748a6a..8a141b1088a 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -20,7 +20,8 @@ ------- """ # **************************************************************************** -# Copyright (C) 2024 David Coudert +# Copyright (C) 2017-2024 Marianna Spyrakou +# 2024 David Coudert # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,6 +30,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.combinat.partitions import AccelAsc_next + class CoTree: r""" @@ -168,54 +171,6 @@ def reset_info(self): self.info = None -def next_partition(P): - r""" - Return the next partition after `P`, if any. - - This is a helper method to method :meth:`cographs`. - - INPUT: - - - ``P`` -- a list encoding a partition of a number `n` - - EXAMPLES:: - - sage: from sage.graphs.cographs import next_partition - sage: P = [1, 1, 1, 1, 1, 1] - sage: while P: - ....: print(P) - ....: P = next_partition(P) - [1, 1, 1, 1, 1, 1] - [1, 1, 1, 1, 2] - [1, 1, 1, 3] - [1, 1, 2, 2] - [1, 1, 4] - [1, 2, 3] - [1, 5] - [2, 2, 2] - [2, 4] - [3, 3] - """ - if len(P) < 2: - raise ValueError("the length of the input partition must be at least 2") - n = sum(P) - if P[0] != n//2: - if P[-1] - P[-2] <= 1: - return P[:-2] + [P[-2] + P[-1]] - - x = P[-2] + 1 - y = P[-1] - 1 - q = y // x - if q > 1: - r = y % x - return P[:-2] + [x]*q + [x + r] - return P[:-2] + [x, y] - - if n == 3 and P[1] != n//2 + n % 2: - return [1, 2] - return None - - def rebuild_node(u, P): r""" Replace the subtree rooted at `u` by a subtree induced by partition `P`. @@ -233,6 +188,8 @@ def rebuild_node(u, P): sage: next(graphs.cographs(3, as_graph=True)).vertices() # indirect doctest [0, 1, 2] """ + if P is None: + print('P is None') u.children = [] # delete the subtree rooted at u for value in P: this_child = CoTree(value) @@ -244,7 +201,7 @@ def rebuild_node(u, P): def find_pivot(T): r""" - Seach for a pivot node in `T`. + Search for a pivot node in `T`. This is a helper method to method :meth:`cographs`. @@ -297,9 +254,11 @@ def next_tree(T): if pivot is None: return False - # Find the next partition induced by the subtree pivot + # Find the next partition induced by the subtree pivot. + # Partitions are represented in ascending order (i.e., `P_i \leq P_{i+1}`) + # and we search for the next partition in lexicographic order. partition = [c.name for c in pivot.children] - P = next_partition(partition) + P = AccelAsc_next(partition) # and rebuild the subtree of pivot accordingly rebuild_node(pivot, P) From 5d1fc2aec090c301d9a187af4eb32d5e45e251df Mon Sep 17 00:00:00 2001 From: David Coudert Date: Tue, 4 Jun 2024 12:59:03 +0200 Subject: [PATCH 6/7] Update src/sage/graphs/cographs.py Co-authored-by: Travis Scrimshaw --- src/sage/graphs/cographs.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index 8a141b1088a..44a0c736060 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -62,21 +62,7 @@ def __init__(self, name='root'): self.info = None self.parent = None - def __repr__(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: from sage.graphs.cographs import CoTree - sage: CoTree(1) - ( 1 ) - sage: CoTree('J') - [ J ] - sage: next(graphs.cographs(4, as_graph=False)) # indirect doctest - [ J ( 0 ) ( 1 ) ( 2 ) ( 3 ) ] - """ - return self.__str__() + __repr__ = __str__ def __str__(self): r""" From c8a1949968e557a98919b901cb646a441e33ec23 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 4 Jun 2024 16:04:36 +0200 Subject: [PATCH 7/7] fix error --- src/sage/graphs/cographs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/cographs.py b/src/sage/graphs/cographs.py index 44a0c736060..b921375f643 100644 --- a/src/sage/graphs/cographs.py +++ b/src/sage/graphs/cographs.py @@ -62,8 +62,6 @@ def __init__(self, name='root'): self.info = None self.parent = None - __repr__ = __str__ - def __str__(self): r""" Return a string representation of ``self``. @@ -90,6 +88,8 @@ def __str__(self): s += last return s + __repr__ = __str__ + def add_child(self, node): r""" Add cotree ``node`` in the list of children of ``self``.