From a7e1e54f6310e3bf6bd4c6caba97712617d6c474 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 27 Mar 2022 18:06:35 +0200 Subject: [PATCH 1/4] trac #33579: ensure random graph generators use parameter seed --- src/sage/graphs/generators/random.py | 112 ++++++++++++++++------- src/sage/graphs/graph_generators_pyx.pyx | 14 ++- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index b6bff1edd0d..a5193667fa5 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -33,15 +33,15 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): - ``p`` -- probability of an edge - - ``seed`` - a ``random.Random`` seed or a Python ``int`` for the random - number generator (default: ``None``). + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) - ``fast`` -- boolean set to True (default) to use the algorithm with time complexity in `O(n+m)` proposed in [BB2005a]_. It is designed for generating large sparse graphs. It is faster than other algorithms for *LARGE* instances (try it to know whether it is useful for you). - - ``algorithm`` -- By default (```algorithm='Sage'``), this function uses the + - ``algorithm`` -- By default (``algorithm='Sage'``), this function uses the algorithm implemented in ```sage.graphs.graph_generators_pyx.pyx``. When ``algorithm='networkx'``, this function calls the NetworkX function ``fast_gnp_random_graph``, unless ``fast=False``, then @@ -105,13 +105,13 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): if 0.0 > p or 1.0 < p: raise ValueError("The probability p must be in [0..1].") - if seed is None: - seed = int(current_randstate().long_seed() % sys.maxsize) if p == 1: from sage.graphs.generators.basic import CompleteGraph return CompleteGraph(n) if algorithm == 'networkx': + if seed is None: + seed = int(current_randstate().long_seed() % sys.maxsize) import networkx if fast: G = networkx.fast_gnp_random_graph(n, p, seed=seed) @@ -121,7 +121,7 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): elif algorithm in ['Sage', 'sage']: # We use the Sage generator from sage.graphs.graph_generators_pyx import RandomGNP as sageGNP - return sageGNP(n, p) + return sageGNP(n, p, seed=seed) else: raise ValueError("'algorithm' must be equal to 'networkx' or to 'Sage'.") @@ -183,7 +183,7 @@ def RandomBarabasiAlbert(n, m, seed=None): import networkx return Graph(networkx.barabasi_albert_graph(int(n), int(m), seed=seed)) -def RandomBipartite(n1, n2, p, set_position=False): +def RandomBipartite(n1, n2, p, set_position=False, seed=None): r""" Returns a bipartite graph with `n1+n2` vertices such that any edge from `[n1]` to `[n2]` exists with probability `p`. @@ -198,6 +198,9 @@ def RandomBipartite(n1, n2, p, set_position=False): assign positions to the vertices so that the set of cardinality `n1` is on the line `y=1` and the set of cardinality `n2` is on the line `y=0`. + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + EXAMPLES:: sage: g = graphs.RandomBipartite(5, 2, 0.5) @@ -235,6 +238,8 @@ def RandomBipartite(n1, n2, p, set_position=False): raise ValueError("parameter p is a probability, and so should be a real value between 0 and 1") if not (n1 > 0 and n2 > 0): raise ValueError("n1 and n2 should be integers strictly greater than 0") + if seed is not None: + set_random_seed(seed) from numpy.random import uniform @@ -261,7 +266,7 @@ def RandomBipartite(n1, n2, p, set_position=False): return g -def RandomRegularBipartite(n1, n2, d1, set_position=False): +def RandomRegularBipartite(n1, n2, d1, set_position=False, seed=None): r""" Return a random regular bipartite graph on `n1 + n2` vertices. @@ -286,6 +291,9 @@ def RandomRegularBipartite(n1, n2, d1, set_position=False): assign positions to the vertices so that the set of cardinality `n1` is on the line `y=1` and the set of cardinality `n2` is on the line `y=0`. + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + EXAMPLES:: sage: g = graphs.RandomRegularBipartite(4, 6, 3) @@ -326,6 +334,8 @@ def RandomRegularBipartite(n1, n2, d1, set_position=False): d2 = (n1 * d1) // n2 if n1 * d1 != n2 * d2: raise ValueError("the product n1 * d1 must be a multiple of n2") + if seed is not None: + set_random_seed(seed) complement = False if d1 > n2/2 or d2 > n1/2: @@ -418,7 +428,7 @@ def RandomRegularBipartite(n1, n2, d1, set_position=False): return G -def RandomBlockGraph(m, k, kmax=None, incidence_structure=False): +def RandomBlockGraph(m, k, kmax=None, incidence_structure=False, seed=None): r""" Return a Random Block Graph. @@ -449,6 +459,9 @@ def RandomBlockGraph(m, k, kmax=None, incidence_structure=False): graph itself, that is the list of the lists of vertices in each block. This is useful for the creation of some hypergraphs. + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + OUTPUT: A Graph when ``incidence_structure==False`` (default), and otherwise an @@ -534,6 +547,8 @@ def RandomBlockGraph(m, k, kmax=None, incidence_structure=False): kmax = k elif kmax < k: raise ValueError("the maximum number `kmax` of vertices in a block must be >= `k`") + if seed is not None: + set_random_seed(seed) if m == 1: # A block graph with a single block is a clique @@ -575,7 +590,7 @@ def RandomBlockGraph(m, k, kmax=None, incidence_structure=False): return BG -def RandomBoundedToleranceGraph(n): +def RandomBoundedToleranceGraph(n, seed=None): r""" Return a random bounded tolerance graph. @@ -595,6 +610,9 @@ def RandomBoundedToleranceGraph(n): - ``n`` -- number of vertices of the random graph. + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + EXAMPLES: Every (bounded) tolerance graph is perfect. Hence, the @@ -619,6 +637,8 @@ def RandomBoundedToleranceGraph(n): """ if n < 0: raise ValueError('the number `n` of vertices must be >= 0') + if seed is not None: + set_random_seed(seed) from sage.graphs.generators.intersection import ToleranceGraph @@ -647,21 +667,17 @@ def RandomGNM(n, m, dense=False, seed=None): - ``dense`` - whether to use NetworkX's dense_gnm_random_graph or gnm_random_graph - - ``seed`` - a ``random.Random`` seed or a Python ``int`` for the random - number generator (default: ``None``). - + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) - EXAMPLES: We show the edge list of a random graph on 5 nodes with - 10 edges. + EXAMPLES: - :: + We show the edge list of a random graph on 5 nodes with 10 edges:: sage: graphs.RandomGNM(5, 10).edges(labels=False) [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] - We plot a random graph on 12 nodes with m = 12. - - :: + We plot a random graph on 12 nodes with m = 12:: sage: gnm = graphs.RandomGNM(12, 12) sage: gnm.show() # long time @@ -809,7 +825,7 @@ def RandomHolmeKim(n, m, p, seed=None): return Graph(networkx.powerlaw_cluster_graph(n, m, p, seed=seed)) -def RandomIntervalGraph(n): +def RandomIntervalGraph(n, seed=None): r""" Returns a random interval graph. @@ -833,8 +849,10 @@ def RandomIntervalGraph(n): INPUT: - - ``n`` (integer) -- the number of vertices in the random - graph. + - ``n`` -- integer; the number of vertices in the random graph + + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) EXAMPLES: @@ -845,7 +863,8 @@ def RandomIntervalGraph(n): sage: g.clique_number() == g.chromatic_number() True """ - + if seed is not None: + set_random_seed(seed) from sage.misc.prandom import random from sage.graphs.generators.intersection import IntervalGraph @@ -1086,7 +1105,7 @@ def pruned_tree(T, f, s): return S -def RandomChordalGraph(n, algorithm="growing", k=None, l=None, f=None, s=None): +def RandomChordalGraph(n, algorithm="growing", k=None, l=None, f=None, s=None, seed=None): r""" Return a random chordal graph of order ``n``. @@ -1151,6 +1170,9 @@ def RandomChordalGraph(n, algorithm="growing", k=None, l=None, f=None, s=None): `0.5`. This parameter is used only when ``algorithm="pruned"``. See :meth:`~sage.graphs.generators.random.pruned_tree` for more details. + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + EXAMPLES:: sage: from sage.graphs.generators.random import RandomChordalGraph @@ -1202,6 +1224,9 @@ def RandomChordalGraph(n, algorithm="growing", k=None, l=None, f=None, s=None): if n < 2: return Graph(n, name="Random Chordal Graph") + if seed is not None: + set_random_seed(seed) + # 1. Generate a random tree of order n T = RandomTree(n) @@ -1303,7 +1328,7 @@ def RandomLobster(n, p, q, seed=None): return Graph(networkx.random_lobster(n, p, q, seed=seed)) -def RandomTree(n): +def RandomTree(n, seed=None): r""" Returns a random tree on `n` nodes numbered `0` through `n-1`. @@ -1320,7 +1345,10 @@ def RandomTree(n): INPUT: - - ``n`` - number of vertices in the tree + - ``n`` -- number of vertices in the tree + + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) EXAMPLES:: @@ -1348,6 +1376,9 @@ def RandomTree(n): if n <= 1: return g + if seed is not None: + set_random_seed(seed) + # create random Prufer code code = [ randint(0,n-1) for i in range(n-2) ] @@ -1499,7 +1530,7 @@ def RandomShell(constructor, seed=None): import networkx return Graph(networkx.random_shell_graph(constructor, seed=seed)) -def RandomToleranceGraph(n): +def RandomToleranceGraph(n, seed=None): r""" Return a random tolerance graph. @@ -1517,7 +1548,10 @@ def RandomToleranceGraph(n): INPUT: - - ``n`` -- number of vertices of the random graph. + - ``n`` -- number of vertices of the random graph + + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) EXAMPLES: @@ -1539,6 +1573,8 @@ def RandomToleranceGraph(n): if n < 0: raise ValueError('the number `n` of vertices must be >= 0') + if seed is not None: + set_random_seed(seed) W = n**2 * 2**n @@ -1783,7 +1819,7 @@ def _contour_and_graph_from_words(pendant_word, forest_word): G.set_embedding(embedding) return word, G -def RandomTriangulation(n, set_position=False, k=3): +def RandomTriangulation(n, set_position=False, k=3, seed=None): r""" Return a random inner triangulation of an outer face of degree ``k`` with ``n`` vertices in total. @@ -1800,6 +1836,9 @@ def RandomTriangulation(n, set_position=False, k=3): - ``set_position`` -- boolean (default ``False``); if set to ``True``, this will compute coordinates for a planar drawing of the graph. + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + OUTPUT: A random graph chosen uniformly among the inner triangulations of a *rooted* @@ -1874,6 +1913,8 @@ def RandomTriangulation(n, set_position=False, k=3): if n < k: raise ValueError("The number 'n' of vertices must be at least the size " "'k' of the outer face.") + if seed is not None: + set_random_seed(seed) from sage.misc.prandom import shuffle pendant_word = [0] * (k-1) + [1] * (k-3) @@ -1921,7 +1962,7 @@ def rotate_word_to_next_occurrence(word): return graph -def blossoming_contour(t, shift=0): +def blossoming_contour(t, shift=0, seed=None): """ Return a random blossoming of a binary tree `t`, as a contour word. @@ -1977,6 +2018,9 @@ def blossoming_contour(t, shift=0): """ if not t: raise ValueError('tree must be non-empty') + if seed is not None: + set_random_seed(seed) + t1, t2 = t leaf_xb = ('xb',) leaf_x = ('x',) @@ -2011,7 +2055,7 @@ def blossoming_contour(t, shift=0): return label + tt1 + label + tt2 + label -def RandomBicubicPlanar(n): +def RandomBicubicPlanar(n, seed=None): """ Return the graph of a random bipartite cubic map with `3 n` edges. @@ -2019,6 +2063,9 @@ def RandomBicubicPlanar(n): `n` -- an integer (at least `1`) + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) + OUTPUT: a graph with multiple edges (no embedding is provided) @@ -2068,6 +2115,9 @@ def RandomBicubicPlanar(n): from sage.rings.finite_rings.integer_mod_ring import Zmod if not n: raise ValueError("n must be at least 1") + if seed is not None: + set_random_seed(seed) + # first pick a random binary tree t = BinaryTrees(n).random_element() diff --git a/src/sage/graphs/graph_generators_pyx.pyx b/src/sage/graphs/graph_generators_pyx.pyx index f37fbd6de45..8e9f40cd85a 100644 --- a/src/sage/graphs/graph_generators_pyx.pyx +++ b/src/sage/graphs/graph_generators_pyx.pyx @@ -15,8 +15,9 @@ AUTHORS: ################################################################################ from sage.misc.randstate cimport random +from sage.misc.randstate import set_random_seed -def RandomGNP(n, p, bint directed=False, bint loops=False): +def RandomGNP(n, p, bint directed=False, bint loops=False, seed=None): r""" Return a random graph or a digraph on `n` nodes. @@ -32,7 +33,10 @@ def RandomGNP(n, p, bint directed=False, bint loops=False): directed or undirected (default) - ``loops`` -- boolean (default: ``False``); whether the random digraph may - have loops or not. This value is used only when ``directed == True``. + have loops or not. This value is used only when ``directed == True`` + + - ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random + number generator (default: ``None``) REFERENCES: @@ -43,8 +47,7 @@ def RandomGNP(n, p, bint directed=False, bint loops=False): EXAMPLES:: sage: from sage.graphs.graph_generators_pyx import RandomGNP - sage: set_random_seed(0) - sage: D = RandomGNP(10, .2, directed=True) + sage: D = RandomGNP(10, .2, directed=True, seed=0) sage: D.num_verts() 10 sage: D.edges(labels=False) @@ -62,6 +65,9 @@ def RandomGNP(n, p, bint directed=False, bint loops=False): """ from sage.graphs.graph import Graph, DiGraph + if seed is not None: + set_random_seed(seed) + # according the sage.misc.randstate.pyx documentation, random # integers are on 31 bits. We thus set the pivot value to p*2^31 cdef float RAND_MAX_f = float(1<<31) From 26fe18687841db9bfcc3ccf101f067f6d984d0fe Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 27 Mar 2022 19:12:06 +0200 Subject: [PATCH 2/4] trac #33579: fix some doctests --- src/sage/graphs/generators/random.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index a5193667fa5..5c778d04b37 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -63,7 +63,7 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): sage: set_random_seed(0) sage: graphs.RandomGNP(6, .4).edges(labels=False) - [(0, 1), (0, 5), (1, 2), (2, 4), (3, 4), (3, 5), (4, 5)] + [(0, 3), (1, 2), (2, 3), (2, 4)] We plot a random graph on 12 nodes with probability `p = .71`:: @@ -98,7 +98,7 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): 243 sage: graphs.RandomGNP(50,.2, algorithm="networkx").size() 260 # 32-bit - 245 # 64-bit + 209 # 64-bit """ if n < 0: raise ValueError("The number of nodes must be positive or null.") From 2f3f304f44af47b3f80fe03c8224bf2a2b40e288 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 5 Apr 2022 18:58:45 +0200 Subject: [PATCH 3/4] trac #33579: fix doctests in sagebook --- .../graphtheory_doctest.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py index fe526d8ec87..8a6fa7609c1 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/graphtheory_doctest.py @@ -219,12 +219,12 @@ sage: # Number of colors used sage: max(color.values()) + 1 - 6 + 5 Sage example in ./graphtheory.tex, line 1635:: sage: P = Permutations([0,1,2,3]); P.random_element() - [2, 0, 1, 3] + [3, 2, 1, 0] Sage example in ./graphtheory.tex, line 1646:: @@ -257,7 +257,7 @@ Sage example in ./graphtheory.tex, line 1697:: sage: best_chromatic_number # Number of colors used - 4 + 5 Sage example in ./graphtheory.tex, line 1718:: @@ -283,7 +283,7 @@ sage: n_colors, coloration = min([greedy_coloring(g, P.random_element()) ....: for i in range(50)], key=lambda c: c[0]) sage: n_colors - 4 + 5 Sage example in ./graphtheory.tex, line 1782:: @@ -342,14 +342,14 @@ Sage example in ./graphtheory.tex, line 2012:: - sage: set_random_seed(3) + sage: set_random_seed(4) Sage example in ./graphtheory.tex, line 2021:: sage: H = graphs.PetersenGraph() sage: G = graphs.RandomGNP(500,0.5) sage: find_induced(H,G) - {0: 0, 1: 4, 2: 3, 3: 7, 4: 35, 5: 10, 6: 67, 7: 108, 8: 240, 9: 39} + {0: 0, 1: 1, 2: 3, 3: 13, 4: 7, 5: 62, 6: 24, 7: 232, 8: 67, 9: 45} Sage example in ./graphtheory.tex, line 2070:: From 6cbdf913dcbef441110c7936fb5f0d70d16be248 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 10 Apr 2022 12:55:33 +0200 Subject: [PATCH 4/4] trac #33579: fix doctest for 32-bit --- src/sage/graphs/generators/random.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 5c778d04b37..de1148f0bf2 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -97,7 +97,7 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): sage: graphs.RandomGNP(50,.2, algorithm="Sage").size() 243 sage: graphs.RandomGNP(50,.2, algorithm="networkx").size() - 260 # 32-bit + 279 # 32-bit 209 # 64-bit """ if n < 0: