@@ -1174,14 +1174,18 @@ def add_edges(self, edges, loops=False):
1174
1174
INPUT:
1175
1175
1176
1176
- ``edges`` -- an iterable of edges, given either as ``(u, v)``
1177
- or ``(u, v, 'label')``
1177
+ or ``(u, v, 'label')``. If an edge is provided in the format
1178
+ ``(u, v)``, the label is set to ``None``.
1178
1179
1179
1180
- ``loops`` -- boolean (default: ``False``); note that this shall
1180
1181
always be set to either ``False`` or ``None`` (since matching covered
1181
1182
graphs are free of loops), in which case all the loops
1182
1183
``(v, v, 'label')`` are removed from the iterator. If ``loops`` is
1183
1184
set to ``True``, a :exc:`ValueError` is thrown.
1184
1185
1186
+ - Please note that all the loops present in the iterator are ignored,
1187
+ provided that ``loops`` is set to ``False`` or ``None``.
1188
+
1185
1189
OUTPUT:
1186
1190
1187
1191
- If ``loops`` is set to ``True``, a :exc:`ValueError` is returned.
@@ -1213,7 +1217,7 @@ def add_edges(self, edges, loops=False):
1213
1217
sage: G = MatchingCoveredGraph(S)
1214
1218
sage: F = [(0, 4), (2, 4), (4, 6), (4, 7)]
1215
1219
sage: G.add_edges(F)
1216
- sage: G.edges(sort=True)
1220
+ sage: G.edges(sort=True, sort_vertices=True )
1217
1221
[(0, 1, None), (0, 3, None), (0, 4, None), (0, 6, None),
1218
1222
(1, 2, None), (1, 4, None), (2, 4, None), (2, 5, None),
1219
1223
(2, 7, None), (3, 4, None), (3, 6, None), (4, 5, None),
@@ -1228,7 +1232,7 @@ def add_edges(self, edges, loops=False):
1228
1232
sage: F = [(0, 9), (1, 8), (2, 9), (3, 8),
1229
1233
....: (4, 9), (5, 8), (6, 9), (7, 8)]
1230
1234
sage: G.add_edges(F)
1231
- sage: G.edges(sort=True)
1235
+ sage: G.edges(sort=True, sort_vertices=True )
1232
1236
[(0, 1, None), (0, 7, None), (0, 9, None), (1, 2, None),
1233
1237
(1, 8, None), (2, 3, None), (2, 9, None), (3, 4, None),
1234
1238
(3, 8, None), (4, 5, None), (4, 9, None), (5, 6, None),
@@ -1243,7 +1247,7 @@ def add_edges(self, edges, loops=False):
1243
1247
sage: F = {(0, 8, None), (1, 10), (4, 11, 'label'),
1244
1248
....: (5, 9), (8, 9), (10, 11)}
1245
1249
sage: G.add_edges(F)
1246
- sage: G.edges(sort=True)
1250
+ sage: G.edges(sort=True, sort_vertices=True )
1247
1251
[(0, 1, None), (0, 3, None), (0, 4, None), (0, 8, None),
1248
1252
(1, 2, None), (1, 5, None), (1, 10, None), (2, 3, None),
1249
1253
(2, 6, None), (3, 7, None), (4, 5, None), (4, 7, None),
@@ -1289,7 +1293,7 @@ def add_edges(self, edges, loops=False):
1289
1293
sage: G = MatchingCoveredGraph(W)
1290
1294
sage: F = [(0, 0), (1, 3), (2, 4)]
1291
1295
sage: G.add_edges(edges=F, loops=False)
1292
- sage: G.edges(sort=True)
1296
+ sage: G.edges(sort=True, sort_vertices=True )
1293
1297
[(0, 1, None), (0, 2, None), (0, 3, None), (0, 4, None),
1294
1298
(0, 5, None), (1, 2, None), (1, 3, None), (1, 5, None),
1295
1299
(2, 3, None), (2, 4, None), (3, 4, None), (4, 5, None)]
@@ -1298,7 +1302,7 @@ def add_edges(self, edges, loops=False):
1298
1302
Traceback (most recent call last):
1299
1303
...
1300
1304
ValueError: loops are not allowed in matching covered graphs
1301
- sage: G.edges(sort=True)
1305
+ sage: G.edges(sort=True, sort_vertices=True )
1302
1306
[(0, 1, None), (0, 2, None), (0, 3, None), (0, 4, None),
1303
1307
(0, 5, None), (1, 2, None), (1, 3, None), (1, 5, None),
1304
1308
(2, 3, None), (2, 4, None), (3, 4, None), (4, 5, None)]
@@ -1325,17 +1329,31 @@ def add_edges(self, edges, loops=False):
1325
1329
(0, 6, None), (1, 2, None), (1, 2, None), (1, 4, None),
1326
1330
(2, 5, None), (2, 7, None), (3, 4, None), (3, 6, None),
1327
1331
(4, 5, None), (5, 7, None), (6, 7, None)]
1332
+ sage: H = [(0, 1)] * 4
1333
+ sage: G.add_edges(H)
1334
+ sage: G.edge_label(0, 1)
1335
+ [None, None, None, None, None, 'label']
1328
1336
1329
1337
TESTS:
1330
1338
1339
+ Providing with a non-iterable of edges::
1340
+
1341
+ sage: K2 = graphs.CompleteGraph(2)
1342
+ sage: G = MatchingCoveredGraph(K2)
1343
+ sage: G.add_edges(1234)
1344
+ Traceback (most recent call last):
1345
+ ...
1346
+ ValueError: expected an iterable of edges,
1347
+ but got a non-iterable object
1348
+
1331
1349
Providing with an edge in ``edges`` that has 0 values to unpack::
1332
1350
1333
1351
sage: W = graphs.WagnerGraph()
1334
1352
sage: G = MatchingCoveredGraph(W)
1335
1353
sage: G.add_edges([()])
1336
1354
Traceback (most recent call last):
1337
1355
...
1338
- ValueError: need more than 0 values to unpack
1356
+ ValueError: need more than 1 value to unpack for edge: ()
1339
1357
1340
1358
Providing with an edge in ``edges`` that has precisely one value to unpack::
1341
1359
@@ -1344,7 +1362,7 @@ def add_edges(self, edges, loops=False):
1344
1362
sage: G.add_edges([(0, )])
1345
1363
Traceback (most recent call last):
1346
1364
...
1347
- ValueError: need more than 1 value to unpack
1365
+ ValueError: need more than 1 value to unpack for edge: (0,)
1348
1366
1349
1367
Providing with an edge in ``edges`` that has more than 3 values to unpack::
1350
1368
@@ -1353,17 +1371,17 @@ def add_edges(self, edges, loops=False):
1353
1371
sage: G.add_edges([(0, 1, 2, 3, 4)])
1354
1372
Traceback (most recent call last):
1355
1373
...
1356
- ValueError: too many values to unpack (expected 2)
1374
+ ValueError: too many values to unpack (expected 2) for edge: (0, 1, 2, 3, 4)
1357
1375
1358
1376
Providing with an edge of unknown data type::
1359
1377
1360
1378
sage: M = graphs.MurtyGraph()
1361
1379
sage: G = MatchingCoveredGraph(M)
1362
- sage: F = ['' , 'edge', None, 1234 ]
1380
+ sage: F = [None , 'edge', None]
1363
1381
sage: G.add_edges(F)
1364
1382
Traceback (most recent call last):
1365
1383
...
1366
- TypeError: input edges is of unknown type
1384
+ TypeError: input edge None is of unknown type
1367
1385
"""
1368
1386
if loops :
1369
1387
raise ValueError ('loops are not allowed in '
@@ -1372,34 +1390,45 @@ def add_edges(self, edges, loops=False):
1372
1390
if not edges : # do nothing
1373
1391
return
1374
1392
1375
- for edge in edges :
1376
- if isinstance (edge , tuple ):
1377
- if len ( edge ) == 0 :
1378
- raise ValueError ( 'need more than 0 values to unpack ' )
1393
+ from collections . abc import Iterable
1394
+ if not isinstance (edges , Iterable ):
1395
+ raise ValueError ( 'expected an iterable of edges, '
1396
+ 'but got a non-iterable object ' )
1379
1397
1380
- elif len (edge ) == 1 :
1381
- raise ValueError ('need more than 1 value to unpack' )
1398
+ links = [] # to extract the nonloop input edges
1399
+ for edge in edges :
1400
+ if hasattr (edge , '__len__' ):
1401
+ if len (edge ) <= 1 :
1402
+ raise ValueError ('need more than 1 value to unpack '
1403
+ f'for edge: { edge } ' )
1382
1404
1383
1405
elif len (edge ) > 3 :
1384
- raise ValueError ('too many values to unpack (expected 2)' )
1406
+ raise ValueError ('too many values to unpack (expected 2) '
1407
+ f'for edge: { edge } ' )
1385
1408
1386
1409
else :
1387
- raise TypeError ('input edges is of unknown type' )
1410
+ raise TypeError (f 'input edge { edge } is of unknown type' )
1388
1411
1389
- # Remove potentially duplicated edges
1390
- edges = list (set (edges ))
1412
+ u , v , l = None , None , None
1391
1413
1392
- # Remove all the loops from edges
1393
- for edge in edges :
1394
- if edge [0 ] == edge [1 ]:
1395
- edges .remove (edge )
1414
+ if len (edge ) == 2 :
1415
+ u , v = edge
1396
1416
1397
- # Check if all the incident vertices of the input edges are existent
1398
- new_vertices = list (set ([x for u , v , * _ in edges for x in [u , v ]]))
1417
+ else :
1418
+ u , v , l = edge
1419
+
1420
+ if u != v :
1421
+ links .append ((u , v , l ))
1399
1422
1400
- for vertex in new_vertices [:]:
1401
- if vertex in self :
1402
- new_vertices .remove (vertex )
1423
+ # If each of the input edges is existent
1424
+ if (self .allows_multiple_edges ()
1425
+ and all (self .has_edge (* edge ) for edge in links )):
1426
+ self ._backend .add_edges (links , self ._directed )
1427
+ return
1428
+
1429
+ # Check if all the incident vertices of the input edges are existent
1430
+ new_vertices = {x for u , v , _ in links for x in (u , v )
1431
+ if x not in self }
1403
1432
1404
1433
# Throw error if the no. of new vertices is odd
1405
1434
if len (new_vertices ) % 2 :
@@ -1408,7 +1437,7 @@ def add_edges(self, edges, loops=False):
1408
1437
1409
1438
try :
1410
1439
G = Graph (self , multiedges = self .allows_multiple_edges ())
1411
- G .add_edges (edges = edges , loops = loops )
1440
+ G .add_edges (edges = links , loops = loops )
1412
1441
1413
1442
# Check if G has a vertex with at most 1 neighbor
1414
1443
if any (len (G .neighbors (v )) <= 1 for v in G ):
@@ -1423,14 +1452,14 @@ def add_edges(self, edges, loops=False):
1423
1452
else :
1424
1453
# Check if the existing perfect matching may be extended to a
1425
1454
# perfect matching of the new graph
1426
- edges_with_two_new_vertices = []
1455
+ links_with_two_new_vertices = []
1427
1456
1428
- for edge in edges :
1457
+ for edge in links :
1429
1458
if edge [0 ] in new_vertices and edge [1 ] in new_vertices :
1430
- edges_with_two_new_vertices .append (edge )
1459
+ links_with_two_new_vertices .append (edge )
1431
1460
1432
- H = Graph (data = edges_with_two_new_vertices , format = 'list_of_edges' )
1433
- M = Graph (self .get_matching ()). union ( Graph ( H . matching () ))
1461
+ M = Graph (data = links_with_two_new_vertices , format = 'list_of_edges' )
1462
+ M . add_edges (self .get_matching ())
1434
1463
1435
1464
# Check if M is a perfect matching of the resulting graph
1436
1465
if (G .order () != 2 * M .size ()):
0 commit comments