@@ -55,6 +55,7 @@ Here is what the module can do:
55
55
:meth:`is_triconnected` | Check whether the graph is triconnected.
56
56
:meth:`spqr_tree` | Return a SPQR-tree representing the triconnected components of the graph.
57
57
:meth:`spqr_tree_to_graph` | Return the graph represented by the SPQR-tree `T`.
58
+ :meth:`minimal_separators` | Return an iterator over the minimal separators of ``G``.
58
59
59
60
Methods
60
61
-------
@@ -134,11 +135,12 @@ def is_connected(G, forbidden_vertices=None):
134
135
if not G.order():
135
136
return True
136
137
138
+ forbidden = None if forbidden_vertices is None else set (forbidden_vertices)
139
+
137
140
try :
138
- return G._backend.is_connected(forbidden_vertices = forbidden_vertices )
141
+ return G._backend.is_connected(forbidden_vertices = forbidden )
139
142
except AttributeError :
140
143
# Search for a vertex in G that is not forbidden
141
- forbidden = set (forbidden_vertices) if forbidden_vertices else set ()
142
144
if forbidden:
143
145
for v in G:
144
146
if v not in forbidden:
@@ -200,6 +202,9 @@ def connected_components(G, sort=None, key=None, forbidden_vertices=None):
200
202
sage: G = graphs.PathGraph(5)
201
203
sage: connected_components(G, sort=True, forbidden_vertices=[2])
202
204
[[0, 1], [3, 4]]
205
+ sage: connected_components(G, sort=True,
206
+ ....: forbidden_vertices=G.neighbor_iterator(2, closed=True))
207
+ [[0], [4]]
203
208
204
209
TESTS:
205
210
@@ -244,7 +249,7 @@ def connected_components(G, sort=None, key=None, forbidden_vertices=None):
244
249
for v in G:
245
250
if v not in seen:
246
251
c = connected_component_containing_vertex(G, v, sort = sort, key = key,
247
- forbidden_vertices = forbidden_vertices )
252
+ forbidden_vertices = seen )
248
253
seen.update(c)
249
254
components.append(c)
250
255
components.sort(key = lambda comp : - len (comp))
@@ -423,12 +428,14 @@ def connected_component_containing_vertex(G, vertex, sort=None, key=None,
423
428
if (not sort) and key:
424
429
raise ValueError (' sort keyword is False, yet a key function is given' )
425
430
431
+ forbidden = None if forbidden_vertices is None else list (forbidden_vertices)
432
+
426
433
try :
427
434
c = list (G._backend.depth_first_search(vertex, ignore_direction = True ,
428
- forbidden_vertices = forbidden_vertices ))
435
+ forbidden_vertices = forbidden ))
429
436
except AttributeError :
430
437
c = list (G.depth_first_search(vertex, ignore_direction = True ,
431
- forbidden_vertices = forbidden_vertices ))
438
+ forbidden_vertices = forbidden ))
432
439
433
440
if sort:
434
441
return sorted (c, key = key)
@@ -1256,6 +1263,104 @@ def is_cut_vertex(G, u, weak=False):
1256
1263
return is_vertex_cut(G, [u], weak = weak)
1257
1264
1258
1265
1266
+ def minimal_separators (G , forbidden_vertices = None ):
1267
+ r """
1268
+ Return an iterator over the minimal separators of ``G``.
1269
+
1270
+ A separator in a graph is a set of vertices whose removal increases the
1271
+ number of connected components. In other words, a separator is a vertex
1272
+ cut. This method implements the algorithm proposed in [BBC2000 ]_.
1273
+ It computes the set `S` of minimal separators of a graph in `O( n^ 3) ` time
1274
+ per separator, and so overall in `O( n^ 3 | S| ) ` time.
1275
+
1276
+ .. WARNING::
1277
+
1278
+ Note that all separators are recorded during the execution of the
1279
+ algorithm and so the memory consumption of this method might be huge.
1280
+
1281
+ INPUT:
1282
+
1283
+ - ``G`` -- an undirected graph
1284
+
1285
+ - ``forbidden_vertices`` -- list ( default: ``None``) ; set of vertices to
1286
+ avoid during the search
1287
+
1288
+ EXAMPLES::
1289
+
1290
+ sage: P = graphs. PathGraph( 5)
1291
+ sage: sorted( sorted( sep) for sep in P. minimal_separators( ))
1292
+ [[1 ], [2 ], [3 ]]
1293
+ sage: C = graphs. CycleGraph( 6)
1294
+ sage: sorted( sorted( sep) for sep in C. minimal_separators( ))
1295
+ [[0, 2 ], [0, 3 ], [0, 4 ], [1, 3 ], [1, 4 ], [1, 5 ], [2, 4 ], [2, 5 ], [3, 5 ]]
1296
+ sage: sorted( sorted( sep) for sep in C. minimal_separators( forbidden_vertices=[0 ]))
1297
+ [[2 ], [3 ], [4 ]]
1298
+ sage: sorted( sorted( sep) for sep in ( P + C) . minimal_separators( ))
1299
+ [[1 ], [2 ], [3 ], [5, 7 ], [5, 8 ], [5, 9 ], [6, 8 ],
1300
+ [6, 9 ], [6, 10 ], [7, 9 ], [7, 10 ], [8, 10 ]]
1301
+ sage: sorted( sorted( sep) for sep in ( P + C) . minimal_separators( forbidden_vertices=[10 ]))
1302
+ [[1 ], [2 ], [3 ], [6 ], [7 ], [8 ]]
1303
+
1304
+ sage: G = graphs. RandomGNP( 10, . 3)
1305
+ sage: all( G. is_vertex_cut( sep) for sep in G. minimal_separators( ))
1306
+ True
1307
+
1308
+ TESTS::
1309
+
1310
+ sage: list( Graph( ) . minimal_separators( ))
1311
+ []
1312
+ sage: list( Graph( 1) . minimal_separators( ))
1313
+ []
1314
+ sage: list( Graph( 2) . minimal_separators( ))
1315
+ []
1316
+ sage: from sage. graphs. connectivity import minimal_separators
1317
+ sage: list( minimal_separators( DiGraph( )))
1318
+ Traceback ( most recent call last) :
1319
+ ...
1320
+ ValueError: the input must be an undirected graph
1321
+ """
1322
+ from sage.graphs.graph import Graph
1323
+ if not isinstance (G, Graph):
1324
+ raise ValueError (" the input must be an undirected graph" )
1325
+
1326
+ if forbidden_vertices is not None and G.order() >= 3 :
1327
+ # Build the subgraph with active vertices
1328
+ G = G.subgraph(set (G).difference(forbidden_vertices), immutable = True )
1329
+
1330
+ if G.order() < 3 :
1331
+ return
1332
+ if not G.is_connected():
1333
+ for cc in G.connected_components(sort = False ):
1334
+ if len (cc) > 2 :
1335
+ yield from minimal_separators(G.subgraph(cc))
1336
+ return
1337
+
1338
+ # Initialization - identify separators needing further inspection
1339
+ cdef list to_explore = []
1340
+ for v in G:
1341
+ # iterate over the connected components of G \ N[v]
1342
+ for comp in G.connected_components(sort = False , forbidden_vertices = G.neighbor_iterator(v, closed = True )):
1343
+ # The vertex boundary of comp in G is a separator
1344
+ nh = G.vertex_boundary(comp)
1345
+ if nh:
1346
+ to_explore.append(frozenset (nh))
1347
+
1348
+ # Generation of all minimal separators
1349
+ cdef set separators = set ()
1350
+ while to_explore:
1351
+ sep = to_explore.pop()
1352
+ if sep in separators:
1353
+ continue
1354
+ yield set (sep)
1355
+ separators.add(sep)
1356
+ for v in sep:
1357
+ # iterate over the connected components of G \ sep \ N(v)
1358
+ for comp in G.connected_components(sort = False , forbidden_vertices = sep.union(G.neighbor_iterator(v))):
1359
+ nh = G.vertex_boundary(comp)
1360
+ if nh:
1361
+ to_explore.append(frozenset (nh))
1362
+
1363
+
1259
1364
def edge_connectivity (G ,
1260
1365
value_only = True ,
1261
1366
implementation = None ,
0 commit comments