diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index e0868b36e21..fd439b98e9a 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -1459,6 +1459,195 @@ def RandomTreePowerlaw(n, gamma=3, tries=1000, seed=None): return False +def RandomKTree(n, k, seed=None): + r""" + Return a random `k`-tree on `n` nodes numbered `0` through `n-1`. + + ALGORITHM: + + The algorithm first generates a complete graph on `k + 1` vertices. + Vertices are subsequently generated by randomly choosing one of the + existing cliques in the graph, and creating a new clique by replacing + one of the vertices in the selected clique with a newly created one. + + INPUT: + + - ``n`` -- number of vertices in the `k`-tree + + - ``k`` -- within a clique each vertex is connected to `k` vertices. `k` + also corresponds to the treewidth of the `k`-tree + + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + + TESTS:: + + sage: g=graphs.RandomKTree(50,5) + sage: g.size() + 235 + sage: g.order() + 50 + sage: g.treewidth() + 5 + sage: graphs.RandomKTree(-5, 5) + Traceback (most recent call last): + ... + ValueError: n must not be negative + sage: graphs.RandomKTree(5, -5) + Traceback (most recent call last): + ... + ValueError: k must not be negative + sage: graphs.RandomKTree(2, 5) + Traceback (most recent call last): + ... + ValueError: n must be greater than k + sage: G = graphs.RandomKTree(50, 0) + sage: G.treewidth() + 0 + + EXAMPLES:: + + sage: G = graphs.RandomKTree(50, 5) + sage: G.treewidth() + 5 + sage: G.show() # not tested + """ + if n < 0: + raise ValueError("n must not be negative") + + if k < 0: + raise ValueError("k must not be negative") + + # A graph with treewidth 0 has no edges + if k == 0: + g = Graph(n, name=f"Random 0-tree") + return g + + if n < k + 1: + raise ValueError("n must be greater than k") + + if seed is not None: + set_random_seed(seed) + + g = Graph(name=f"Random {k}-tree") + g.add_clique(list(range(k + 1))) + + cliques = [list(range(k+1))] + + # Randomly choose a row, and copy 1 of the cliques + # One of those vertices is then replaced with a new vertex + for newVertex in range(k + 1, n): + copiedClique = cliques[randint(0, len(cliques)-1)].copy() + copiedClique[randint(0, k)] = newVertex + cliques.append(copiedClique) + for u in copiedClique: + if u != newVertex: + g.add_edge(u, newVertex) + return g + + +def RandomPartialKTree(n, k, x, seed=None): + r""" + Return a random partial `k`-tree on `n` nodes. + + A partial `k`-tree is defined as a subgraph of a `k`-tree. This can also be + described as a graph with treewidth at most `k`. + + INPUT: + + - ``n`` -- number of vertices in the `k`-tree + + - ``k`` -- within a clique each vertex is connected to `k` vertices. `k` + also corresponds to the treewidth of the `k`-tree + + - ``x`` -- how many edges are deleted from the `k`-tree + + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + + TESTS:: + + sage: g=graphs.RandomPartialKTree(50,5,2) + sage: g.order() + 50 + sage: g.size() + 233 + sage: g.treewidth() + 5 + sage: graphs.RandomPartialKTree(-5, 5, 2) + Traceback (most recent call last): + ... + ValueError: n must not be negative + sage: graphs.RandomPartialKTree(5, -5, 2) + Traceback (most recent call last): + ... + ValueError: k must not be negative + sage: G = graphs.RandomPartialKTree(2, 5, 2) + Traceback (most recent call last): + ... + ValueError: n must be greater than k + sage: G = graphs.RandomPartialKTree(5, 2, 100) + Traceback (most recent call last): + ... + ValueError: x must be less than the number of edges in the `k`-tree with `n` nodes + sage: G = graphs.RandomPartialKTree(50, 0, 0) + sage: G.treewidth() + 0 + sage: G = graphs.RandomPartialKTree(5, 2, 7) + sage: G.treewidth() + 0 + sage: G.size() + 0 + + EXAMPLES:: + + sage: G = graphs.RandomPartialKTree(50,5,2) + sage: G.treewidth() + 5 + sage: G.show() # not tested + """ + if n < 0: + raise ValueError("n must not be negative") + + if k < 0: + raise ValueError("k must not be negative") + + # A graph with treewidth 0 has no edges + if k == 0: + g = Graph(n, name=f"Random partial 0-tree") + return g + + if n < k + 1: + raise ValueError("n must be greater than k") + + if seed is not None: + set_random_seed(seed) + + # This formula calculates how many edges are in a `k`-tree with `n` nodes + edgesInKTree = (k ^ 2 + k) / 2 + (n - k - 1) * k + + # Check that x doesn't delete too many edges + if x > edgesInKTree: + raise ValueError("x must be less than the number of edges in the `k`-tree with `n` nodes") + + # The graph will have no edges + if x == edgesInKTree: + g = Graph(n, name=f"Random partial {k}-tree") + return g + + g = RandomKTree(n, k, seed) + + from sage.misc.prandom import shuffle + + edges = list(g.edges()) + # Deletes x random edges from the graph + shuffle(edges) + g.delete_edges(edges[:x]) + + g.name(f"Random partial {k}-tree") + return g + + def RandomRegular(d, n, seed=None): r""" Return a random `d`-regular graph on `n` vertices, or ``False`` on failure. diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index b7b5d350250..05c92d7e841 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -361,6 +361,8 @@ def wrap_name(x): "RandomHolmeKim", "RandomChordalGraph", "RandomIntervalGraph", + "RandomKTree", + "RandomPartialKTree", "RandomLobster", "RandomNewmanWattsStrogatz", "RandomRegular", @@ -2755,6 +2757,8 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None RandomNewmanWattsStrogatz = staticmethod(random.RandomNewmanWattsStrogatz) RandomRegular = staticmethod(random.RandomRegular) RandomShell = staticmethod(random.RandomShell) + RandomKTree = staticmethod(random.RandomKTree) + RandomPartialKTree = staticmethod(random.RandomPartialKTree) RandomToleranceGraph = staticmethod(random.RandomToleranceGraph) RandomTreePowerlaw = staticmethod(random.RandomTreePowerlaw) RandomTree = staticmethod(random.RandomTree)