Skip to content

Commit 3deb1cb

Browse files
committed
add parameter immutable to random digraph generators
1 parent c9dd1e8 commit 3deb1cb

File tree

2 files changed

+79
-56
lines changed

2 files changed

+79
-56
lines changed

src/sage/graphs/digraph_generators.py

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ def Kautz(self, k, D, vertices='strings'):
13351335
G.name("Kautz digraph (k={}, D={})".format(k, D))
13361336
return G
13371337

1338-
def RandomDirectedAcyclicGraph(self, n, p, weight_max=None):
1338+
def RandomDirectedAcyclicGraph(self, n, p, weight_max=None, immutable=False):
13391339
r"""
13401340
Return a random (weighted) directed acyclic graph of order `n`.
13411341
@@ -1355,6 +1355,9 @@ def RandomDirectedAcyclicGraph(self, n, p, weight_max=None):
13551355
unweighted. When ``weight_max`` is set to a positive integer, edges
13561356
are assigned a random integer weight between ``1`` and ``weight_max``.
13571357
1358+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1359+
immutable or mutable digraph.
1360+
13581361
EXAMPLES::
13591362
13601363
sage: D = digraphs.RandomDirectedAcyclicGraph(5, .5); D
@@ -1405,21 +1408,22 @@ def RandomDirectedAcyclicGraph(self, n, p, weight_max=None):
14051408
pp = int(round(float(p * RAND_MAX_f)))
14061409

14071410
if weight_max is None:
1408-
D = DiGraph(n, name=f"RandomDAG({n}, {p})")
1409-
D.add_edges((i, j) for i in range(n) for j in range(i) if random() < pp)
1411+
name = f"RandomDAG({n}, {p})"
1412+
edges = ((i, j) for i in range(n) for j in range(i) if random() < pp)
14101413

14111414
else:
14121415
from sage.rings.integer_ring import ZZ
14131416
if weight_max in ZZ and weight_max < 1:
14141417
raise ValueError("parameter weight_max must be a positive integer")
14151418

1416-
D = DiGraph(n, name=f"RandomWeightedDAG({n}, {p}, {weight_max})")
1417-
D.add_edges((i, j, randint(1, weight_max))
1418-
for i in range(n) for j in range(i) if random() < pp)
1419+
name = f"RandomWeightedDAG({n}, {p}, {weight_max})"
1420+
edges = ((i, j, randint(1, weight_max))
1421+
for i in range(n) for j in range(i) if random() < pp)
14191422

1420-
return D
1423+
return DiGraph([range(n), edges], format='vertices_and_edges',
1424+
name=name, immutable=immutable)
14211425

1422-
def RandomDirectedGN(self, n, kernel=None, seed=None):
1426+
def RandomDirectedGN(self, n, kernel=None, seed=None, immutable=False):
14231427
r"""
14241428
Return a random growing network (GN) digraph with `n` vertices.
14251429
@@ -1439,6 +1443,9 @@ def RandomDirectedGN(self, n, kernel=None, seed=None):
14391443
- ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the
14401444
random number generator (default: ``None``)
14411445
1446+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1447+
immutable or mutable digraph.
1448+
14421449
EXAMPLES::
14431450
14441451
sage: # needs networkx
@@ -1458,9 +1465,10 @@ def RandomDirectedGN(self, n, kernel=None, seed=None):
14581465
if seed is None:
14591466
seed = int(current_randstate().long_seed() % sys.maxsize)
14601467
import networkx
1461-
return DiGraph(networkx.gn_graph(n, kernel, seed=seed))
1468+
return DiGraph(networkx.gn_graph(n, kernel, seed=seed),
1469+
immutable=immutable)
14621470

1463-
def RandomDirectedGNC(self, n, seed=None):
1471+
def RandomDirectedGNC(self, n, seed=None, immutable=False):
14641472
r"""
14651473
Return a random growing network with copying (GNC) digraph with `n`
14661474
vertices.
@@ -1478,6 +1486,9 @@ def RandomDirectedGNC(self, n, seed=None):
14781486
- ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the
14791487
random number generator (default: ``None``)
14801488
1489+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1490+
immutable or mutable digraph.
1491+
14811492
EXAMPLES::
14821493
14831494
sage: # needs networkx
@@ -1491,9 +1502,9 @@ def RandomDirectedGNC(self, n, seed=None):
14911502
if seed is None:
14921503
seed = int(current_randstate().long_seed() % sys.maxsize)
14931504
import networkx
1494-
return DiGraph(networkx.gnc_graph(n, seed=seed))
1505+
return DiGraph(networkx.gnc_graph(n, seed=seed), immutable=immutable)
14951506

1496-
def RandomDirectedGNP(self, n, p, loops=False, seed=None):
1507+
def RandomDirectedGNP(self, n, p, loops=False, seed=None, immutable=False):
14971508
r"""
14981509
Return a random digraph on `n` nodes.
14991510
@@ -1512,6 +1523,9 @@ def RandomDirectedGNP(self, n, p, loops=False, seed=None):
15121523
- ``seed`` -- integer (default: ``None``); seed for random number
15131524
generator
15141525
1526+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1527+
immutable or mutable digraph.
1528+
15151529
PLOTTING: When plotting, this graph will use the default spring-layout
15161530
algorithm, unless a position dictionary is specified.
15171531
@@ -1530,9 +1544,10 @@ def RandomDirectedGNP(self, n, p, loops=False, seed=None):
15301544
if seed is None:
15311545
seed = current_randstate().long_seed()
15321546

1533-
return RandomGNP(n, p, directed=True, loops=loops, seed=seed)
1547+
return RandomGNP(n, p, directed=True, loops=loops, seed=seed,
1548+
immutable=immutable)
15341549

1535-
def RandomDirectedGNM(self, n, m, loops=False):
1550+
def RandomDirectedGNM(self, n, m, loops=False, immutable=False):
15361551
r"""
15371552
Return a random labelled digraph on `n` nodes and `m` arcs.
15381553
@@ -1544,6 +1559,9 @@ def RandomDirectedGNM(self, n, m, loops=False):
15441559
15451560
- ``loops`` -- boolean (default: ``False``); whether to allow loops
15461561
1562+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1563+
immutable or mutable digraph.
1564+
15471565
PLOTTING: When plotting, this graph will use the default spring-layout
15481566
algorithm, unless a position dictionary is specified.
15491567
@@ -1586,10 +1604,6 @@ def RandomDirectedGNM(self, n, m, loops=False):
15861604
# When the graph is dense, we actually compute its complement. This will
15871605
# prevent us from drawing the same pair u,v too many times.
15881606

1589-
from sage.misc.prandom import _pyrand
1590-
rand = _pyrand()
1591-
D = DiGraph(n, loops=loops)
1592-
15931607
# Ensuring the parameters n,m make sense.
15941608
#
15951609
# If the graph is dense, we actually want to build its complement. We
@@ -1630,9 +1644,10 @@ def RandomDirectedGNM(self, n, m, loops=False):
16301644

16311645
adj = {i: dict() for i in range(n)}
16321646

1633-
# We fill the dictionary structure, but add the corresponding edge in
1634-
# the graph only if is_dense is False. If it is true, we will add the
1635-
# edges in a second phase.
1647+
# We fill the dictionary structure.
1648+
1649+
from sage.misc.prandom import _pyrand
1650+
rand = _pyrand()
16361651

16371652
while m > 0:
16381653

@@ -1648,21 +1663,19 @@ def RandomDirectedGNM(self, n, m, loops=False):
16481663
if (u != v or loops) and (v not in adj[u]):
16491664
adj[u][v] = 1
16501665
m -= 1
1651-
if not is_dense:
1652-
D.add_edge(u, v)
16531666

1654-
# If is_dense is True, it means the graph has not been built. We fill D
1655-
# with the complement of the edges stored in the adj dictionary
1667+
# If is_dense is True, we fill the digraph with the complement of the
1668+
# edges stored in the adj dictionary
16561669

16571670
if is_dense:
1658-
for u in range(n):
1659-
for v in range(n):
1660-
if ((u != v) or loops) and (v not in adj[u]):
1661-
D.add_edge(u, v)
1671+
edges = ((u, v) for u in range(n) for v in range(n)
1672+
if ((u != v) or loops) and (v not in adj[u]))
1673+
return DiGraph([range(n), edges], format='vertices_and_edges',
1674+
loops=loops, immutable=immutable)
16621675

1663-
return D
1676+
return DiGraph(adj, format='dict_of_lists', loops=loops)
16641677

1665-
def RandomDirectedGNR(self, n, p, seed=None):
1678+
def RandomDirectedGNR(self, n, p, seed=None, immutable=False):
16661679
r"""
16671680
Return a random growing network with redirection (GNR) digraph
16681681
with `n` vertices and redirection probability `p`.
@@ -1682,6 +1695,9 @@ def RandomDirectedGNR(self, n, p, seed=None):
16821695
- ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the
16831696
random number generator (default: ``None``)
16841697
1698+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1699+
immutable or mutable digraph.
1700+
16851701
EXAMPLES::
16861702
16871703
sage: # needs networkx
@@ -1695,9 +1711,9 @@ def RandomDirectedGNR(self, n, p, seed=None):
16951711
if seed is None:
16961712
seed = int(current_randstate().long_seed() % sys.maxsize)
16971713
import networkx
1698-
return DiGraph(networkx.gnr_graph(n, p, seed=seed))
1714+
return DiGraph(networkx.gnr_graph(n, p, seed=seed), immutable=immutable)
16991715

1700-
def RandomSemiComplete(self, n):
1716+
def RandomSemiComplete(self, n, immutable=False):
17011717
r"""
17021718
Return a random semi-complete digraph on `n` vertices.
17031719
@@ -1717,6 +1733,9 @@ def RandomSemiComplete(self, n):
17171733
17181734
- ``n`` -- integer; the number of nodes
17191735
1736+
- ``immutable`` -- boolean (default: ``False``); whether to return an
1737+
immutable or mutable digraph.
1738+
17201739
.. SEEALSO::
17211740
17221741
- :meth:`~sage.graphs.digraph_generators.DiGraphGenerators.Complete`
@@ -1734,22 +1753,26 @@ def RandomSemiComplete(self, n):
17341753
...
17351754
ValueError: the number of vertices cannot be strictly negative
17361755
"""
1737-
G = DiGraph(n, name="Random Semi-Complete digraph")
1756+
if n < 0:
1757+
raise ValueError('the number of vertices cannot be strictly negative')
17381758

17391759
# For each pair u,v we choose a random number ``coin`` in [1,3].
17401760
# We select edge `(u,v)` if `coin==1` or `coin==2`.
17411761
# We select edge `(v,u)` if `coin==2` or `coin==3`.
17421762
import itertools
17431763
from sage.misc.prandom import randint
1744-
for u, v in itertools.combinations(range(n), 2):
1745-
coin = randint(1, 3)
1746-
if coin <= 2:
1747-
G.add_edge(u, v)
1748-
if coin >= 2:
1749-
G.add_edge(v, u)
17501764

1751-
G._circle_embedding(list(range(n)))
1765+
def edges():
1766+
for u, v in itertools.combinations(range(n), 2):
1767+
coin = randint(1, 3)
1768+
if coin <= 2:
1769+
yield (u, v)
1770+
if coin >= 2:
1771+
yield (v, u)
17521772

1773+
G = DiGraph([range(n), edges()], format='vertices_and_edges',
1774+
immutable=immutable, name="Random Semi-Complete digraph")
1775+
G._circle_embedding(list(range(n)))
17531776
return G
17541777

17551778
# ##############################################################################

src/sage/graphs/graph_generators_pyx.pyx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ from sage.misc.randstate cimport random
1717
from sage.misc.randstate import set_random_seed
1818

1919

20-
def RandomGNP(n, p, bint directed=False, bint loops=False, seed=None):
20+
def RandomGNP(n, p, bint directed=False, bint loops=False, seed=None,
21+
immutable=False):
2122
r"""
2223
Return a random graph or a digraph on `n` nodes.
2324
@@ -38,6 +39,9 @@ def RandomGNP(n, p, bint directed=False, bint loops=False, seed=None):
3839
- ``seed`` -- a ``random.Random`` seed or a Python ``int`` for the random
3940
number generator (default: ``None``)
4041
42+
- ``immutable`` -- boolean (default: ``False``); whether to return an
43+
immutable or mutable (di)graph.
44+
4145
REFERENCES:
4246
4347
- [ER1959]_
@@ -51,7 +55,8 @@ def RandomGNP(n, p, bint directed=False, bint loops=False, seed=None):
5155
sage: D.num_verts()
5256
10
5357
sage: D.edges(sort=True, labels=False)
54-
[(0, 2), (0, 5), (1, 5), (1, 7), (4, 1), (4, 2), (4, 9), (5, 0), (5, 2), (5, 3), (5, 7), (6, 5), (7, 1), (8, 2), (8, 6), (9, 4)]
58+
[(0, 3), (0, 6), (1, 7), (1, 9), (4, 6), (4, 7), (5, 4), (5, 6),
59+
(5, 8), (5, 9), (6, 3), (7, 2), (7, 9), (8, 5), (9, 1), (9, 5)]
5560
5661
TESTS::
5762
@@ -72,23 +77,18 @@ def RandomGNP(n, p, bint directed=False, bint loops=False, seed=None):
7277
cdef int pp = int(round(float(p * RAND_MAX_f)))
7378

7479
if directed:
75-
from sage.graphs.digraph import DiGraph
76-
G = DiGraph(loops=loops)
80+
from sage.graphs.digraph import DiGraph as GT
7781
else:
78-
from sage.graphs.graph import Graph
79-
G = Graph()
8082
if loops:
8183
raise ValueError("parameter 'loops' can be set to True only when 'directed' is True")
82-
G.name('Random' + ('Directed' if directed else '') + 'GNP(%s,%s)' % (n, p))
84+
from sage.graphs.graph import Graph as GT
8385

84-
G.add_vertices(range(n))
86+
name = 'Random' + ('Directed' if directed else '') + 'GNP(%s,%s)' % (n, p)
8587

86-
# Standard random GNP generator for Graph and DiGraph
8788
cdef int i, j
88-
for i in range(n):
89-
for j in range((0 if directed else i + 1), n):
90-
if random() < pp:
91-
if i != j or loops:
92-
G.add_edge(i, j)
89+
edges = ((i, j) for i in range(n)
90+
for j in range((0 if directed else i + 1), n)
91+
if (i != j or loops) and random() < pp)
9392

94-
return G
93+
return GT([range(n), edges], format='vertices_and_edges',
94+
loops=directed and loops, name=name, immutable=immutable)

0 commit comments

Comments
 (0)