1
1
from .utils import *
2
2
import random
3
+ from typing import TypeVar , Callable
4
+
5
+
6
+ __all__ = ["Edge" , "Graph" ]
3
7
4
8
5
9
class Edge :
6
10
"""Class Edge: A class of the edge in the graph"""
11
+
7
12
def __init__ (self , u , v , w ):
8
13
"""__init__(self, u, v, w) -> None
9
14
Initialize a edge.
@@ -26,11 +31,13 @@ def unweighted_edge(edge):
26
31
"""unweighted_edge(edge) -> str
27
32
Return a string to output the edge without weight. The string contains the start vertex, end vertex(u,v) and splits with space.
28
33
"""
29
- return '%d %d' % (edge .start ,edge .end )
34
+ return '%d %d' % (edge .start , edge .end )
35
+
30
36
31
37
class Graph :
32
38
"""Class Graph: A class of the graph
33
39
"""
40
+
34
41
def __init__ (self , point_count , directed = False ):
35
42
"""__init__(self, point_count) -> None
36
43
Initialize a graph.
@@ -49,6 +56,17 @@ def edge_count(self):
49
56
cnt //= 2
50
57
return cnt
51
58
59
+ def to_matrix (self , ** kwargs ):
60
+ """to_matrix(self, **kwargs) -> GraphMatrix
61
+ Convert the graph to adjacency matrix.
62
+ **kwargs(Keyword args):
63
+ int default = -1 -> the default value when the edge does not exist.
64
+ Any merge(Any, Edge)
65
+ = lambda val, edge: edge.weight
66
+ -> the mapping from the old values in matrix and the edges to the new values in matrix.
67
+ """
68
+ return GraphMatrix (self , ** kwargs )
69
+
52
70
def to_str (self , ** kwargs ):
53
71
"""to_str(self, **kwargs) -> str
54
72
Convert the graph to string with format. Splits with "\n "
@@ -66,7 +84,8 @@ def to_str(self, **kwargs):
66
84
edge_buf = []
67
85
for edge in self .iterate_edges ():
68
86
edge_buf .append (
69
- Edge (new_node_id [edge .start ], new_node_id [edge .end ], edge .weight ))
87
+ Edge (new_node_id [edge .start ], new_node_id [edge .end ],
88
+ edge .weight ))
70
89
random .shuffle (edge_buf )
71
90
for edge in edge_buf :
72
91
if not self .directed and random .randint (0 , 1 ) == 0 :
@@ -164,9 +183,10 @@ def tree(point_count, chain=0, flower=0, **kwargs):
164
183
if not list_like (weight_limit ):
165
184
weight_limit = (1 , weight_limit )
166
185
weight_gen = kwargs .get (
167
- "weight_gen" , lambda : random .randint (
168
- weight_limit [0 ], weight_limit [1 ]))
169
- father_gen = kwargs .get ("father_gen" , lambda cur : random .randrange (1 , cur ))
186
+ "weight_gen" ,
187
+ lambda : random .randint (weight_limit [0 ], weight_limit [1 ]))
188
+ father_gen = kwargs .get ("father_gen" ,
189
+ lambda cur : random .randrange (1 , cur ))
170
190
171
191
if not 0 <= chain <= 1 or not 0 <= flower <= 1 :
172
192
raise Exception ("chain and flower must be between 0 and 1" )
@@ -213,33 +233,35 @@ def binary_tree(point_count, left=0, right=0, **kwargs):
213
233
if not list_like (weight_limit ):
214
234
weight_limit = (1 , weight_limit )
215
235
weight_gen = kwargs .get (
216
- "weight_gen" , lambda : random . randint (
217
- weight_limit [0 ], weight_limit [1 ]))
236
+ "weight_gen" ,
237
+ lambda : random . randint ( weight_limit [0 ], weight_limit [1 ]))
218
238
219
239
if not 0 <= left <= 1 or not 0 <= right <= 1 :
220
240
raise Exception ("left and right must be between 0 and 1" )
221
241
if left + right > 1 :
222
242
raise Exception ("left plus right must be smaller than 1" )
223
-
224
- can_left = [1 ]
225
- can_right = [1 ]
243
+
244
+ can_left = [1 ]
245
+ can_right = [1 ]
226
246
graph = Graph (point_count , directed )
227
247
for i in range (2 , point_count + 1 ):
228
248
edge_pos = random .random ()
229
249
node = 0
230
250
# Left
231
- if edge_pos < left or left + right < edge_pos <= (1.0 - left - right ) / 2 :
232
- point_index = random .randint (0 ,len (can_left )- 1 )
251
+ if edge_pos < left or left + right < edge_pos <= (1.0 - left -
252
+ right ) / 2 :
253
+ point_index = random .randint (0 , len (can_left ) - 1 )
233
254
node = can_left [point_index ]
234
- del_last_node = can_left .pop () # Save a copy of the last element
255
+ del_last_node = can_left .pop (
256
+ ) # Save a copy of the last element
235
257
if point_index < len (can_left ):
236
258
# If the chosen element isn't the last one,
237
259
# Copy the last one to the position of the chosen one
238
260
can_left [point_index ] = del_last_node
239
261
# Right
240
262
else :
241
- # elif left <= edge_pos <= left + right or (1.0 - left - right) / 2 < edge_pos < 1:
242
- point_index = random .randint (0 ,len (can_right )- 1 )
263
+ # elif left <= edge_pos <= left + right or (1.0 - left - right) / 2 < edge_pos < 1:
264
+ point_index = random .randint (0 , len (can_right ) - 1 )
243
265
node = can_right [point_index ]
244
266
del_last_node = can_right .pop ()
245
267
if point_index < len (can_right ):
@@ -278,16 +300,17 @@ def graph(point_count, edge_count, **kwargs):
278
300
if not list_like (weight_limit ):
279
301
weight_limit = (1 , weight_limit )
280
302
weight_gen = kwargs .get (
281
- "weight_gen" , lambda : random . randint (
282
- weight_limit [0 ], weight_limit [1 ]))
303
+ "weight_gen" ,
304
+ lambda : random . randint ( weight_limit [0 ], weight_limit [1 ]))
283
305
graph = Graph (point_count , directed )
284
306
used_edges = set ()
285
307
i = 0
286
308
while i < edge_count :
287
309
u = random .randint (1 , point_count )
288
310
v = random .randint (1 , point_count )
289
311
290
- if (not self_loop and u == v ) or (not repeated_edges and (u , v ) in used_edges ):
312
+ if (not self_loop and u == v ) or (not repeated_edges and
313
+ (u , v ) in used_edges ):
291
314
# Then we generate a new pair of nodes
292
315
continue
293
316
@@ -318,9 +341,11 @@ def DAG(point_count, edge_count, **kwargs):
318
341
-> the generator of the weights. It should return the weight. The default way is to use the random.randint()
319
342
"""
320
343
if edge_count < point_count - 1 :
321
- raise Exception ("the number of edges of connected graph must more than the number of nodes - 1" )
344
+ raise Exception (
345
+ "the number of edges of connected graph must more than the number of nodes - 1"
346
+ )
322
347
323
- self_loop = kwargs .get ("self_loop" , False ) # DAG default has no loop
348
+ self_loop = kwargs .get ("self_loop" , False ) # DAG default has no loop
324
349
repeated_edges = kwargs .get ("repeated_edges" , True )
325
350
loop = kwargs .get ("loop" , False )
326
351
if not repeated_edges :
@@ -332,21 +357,22 @@ def DAG(point_count, edge_count, **kwargs):
332
357
if not list_like (weight_limit ):
333
358
weight_limit = (1 , weight_limit )
334
359
weight_gen = kwargs .get (
335
- "weight_gen" , lambda : random . randint (
336
- weight_limit [0 ], weight_limit [1 ]))
337
-
360
+ "weight_gen" ,
361
+ lambda : random . randint ( weight_limit [0 ], weight_limit [1 ]))
362
+
338
363
used_edges = set ()
339
- edge_buf = list (Graph .tree (point_count , weight_gen = weight_gen ).iterate_edges ())
364
+ edge_buf = list (
365
+ Graph .tree (point_count , weight_gen = weight_gen ).iterate_edges ())
340
366
graph = Graph (point_count , directed = True )
341
367
342
368
for edge in edge_buf :
343
369
if loop and random .randint (1 , 2 ) == 1 :
344
370
edge .start , edge .end = edge .end , edge .start
345
371
graph .add_edge (edge .start , edge .end , weight = edge .weight )
346
-
372
+
347
373
if not repeated_edges :
348
374
used_edges .add ((edge .start , edge .end ))
349
-
375
+
350
376
i = point_count - 1
351
377
while i < edge_count :
352
378
u = random .randint (1 , point_count )
@@ -355,7 +381,8 @@ def DAG(point_count, edge_count, **kwargs):
355
381
if not loop and u > v :
356
382
u , v = v , u
357
383
358
- if (not self_loop and u == v ) or (not repeated_edges and (u , v ) in used_edges ):
384
+ if (not self_loop and u == v ) or (not repeated_edges and
385
+ (u , v ) in used_edges ):
359
386
# Then we generate a new pair of nodes
360
387
continue
361
388
@@ -383,8 +410,10 @@ def UDAG(point_count, edge_count, **kwargs):
383
410
= lambda: random.randint(weight_limit[0], weight_limit[1])
384
411
-> the generator of the weights. It should return the weight. The default way is to use the random.randint()
385
412
"""
386
- if edge_count < point_count - 1 :
387
- raise Exception ("the number of edges of connected graph must more than the number of nodes - 1" )
413
+ if edge_count < point_count - 1 :
414
+ raise Exception (
415
+ "the number of edges of connected graph must more than the number of nodes - 1"
416
+ )
388
417
389
418
self_loop = kwargs .get ("self_loop" , True )
390
419
repeated_edges = kwargs .get ("repeated_edges" , True )
@@ -397,23 +426,24 @@ def UDAG(point_count, edge_count, **kwargs):
397
426
if not list_like (weight_limit ):
398
427
weight_limit = (1 , weight_limit )
399
428
weight_gen = kwargs .get (
400
- "weight_gen" , lambda : random . randint (
401
- weight_limit [0 ], weight_limit [1 ]))
402
-
429
+ "weight_gen" ,
430
+ lambda : random . randint ( weight_limit [0 ], weight_limit [1 ]))
431
+
403
432
used_edges = set ()
404
433
graph = Graph .tree (point_count , weight_gen = weight_gen , directed = False )
405
434
406
435
for edge in graph .iterate_edges ():
407
436
if not repeated_edges :
408
437
used_edges .add ((edge .start , edge .end ))
409
438
used_edges .add ((edge .end , edge .start ))
410
-
439
+
411
440
i = point_count - 1
412
441
while i < edge_count :
413
442
u = random .randint (1 , point_count )
414
443
v = random .randint (1 , point_count )
415
444
416
- if (not self_loop and u == v ) or (not repeated_edges and (u , v ) in used_edges ):
445
+ if (not self_loop and u == v ) or (not repeated_edges and
446
+ (u , v ) in used_edges ):
417
447
# Then we generate a new pair of nodes
418
448
continue
419
449
@@ -459,8 +489,8 @@ def hack_spfa(point_count, **kwargs):
459
489
if not list_like (weight_limit ):
460
490
weight_limit = (1 , weight_limit )
461
491
weight_gen = kwargs .get (
462
- "weight_gen" , lambda : random . randint (
463
- weight_limit [0 ], weight_limit [1 ]))
492
+ "weight_gen" ,
493
+ lambda : random . randint ( weight_limit [0 ], weight_limit [1 ]))
464
494
465
495
point_to_skip = point_count + 3
466
496
graph = Graph (point_count , directed )
@@ -470,15 +500,18 @@ def hack_spfa(point_count, **kwargs):
470
500
471
501
for i in range (1 , half ):
472
502
(x , y ) = (i , i + 1 )
473
- graph .add_edge (x + (x >= point_to_skip ), y +
474
- (y >= point_to_skip ), weight = weight_gen ())
503
+ graph .add_edge (x + (x >= point_to_skip ),
504
+ y + (y >= point_to_skip ),
505
+ weight = weight_gen ())
475
506
(x , y ) = (i + half , i + half + 1 )
476
- graph .add_edge (x + (x >= point_to_skip ), y +
477
- (y >= point_to_skip ), weight = weight_gen ())
507
+ graph .add_edge (x + (x >= point_to_skip ),
508
+ y + (y >= point_to_skip ),
509
+ weight = weight_gen ())
478
510
for i in range (1 , half + 1 ):
479
511
(x , y ) = (i , i + half )
480
- graph .add_edge (x + (x >= point_to_skip ), y +
481
- (y >= point_to_skip ), weight = weight_gen ())
512
+ graph .add_edge (x + (x >= point_to_skip ),
513
+ y + (y >= point_to_skip ),
514
+ weight = weight_gen ())
482
515
483
516
for i in range (extraedg ):
484
517
u = random .randint (1 , point_count )
@@ -495,3 +528,39 @@ def _calc_max_edge(point_count, directed, self_loop):
495
528
if self_loop :
496
529
max_edge += point_count
497
530
return max_edge
531
+
532
+
533
+ class GraphMatrix :
534
+ """
535
+ Class GraphMatrix: A class of the graph represented by adjacency matrix.
536
+
537
+ *Deprecation warning: This class may be removed after a generic matrix class is implemented in the project.*
538
+ """
539
+
540
+ T = TypeVar ('T' )
541
+
542
+ def __init__ (self ,
543
+ graph : Graph ,
544
+ default : T = - 1 ,
545
+ merge : Callable [[T , Edge ],
546
+ T ] = lambda val , edge : edge .weight ):
547
+ """
548
+ Args:
549
+ graph: the graph to convert,
550
+ default: the default value when the edge does not exist,
551
+ merge: the mapping from the old values in matrix and the edges to the new values in matrix.
552
+ """
553
+ n = len (graph .edges )
554
+ self .matrix = [[default for _ in range (n )] for _ in range (n )]
555
+ for edge in graph .iterate_edges ():
556
+ self .matrix [edge .start ][edge .end ] = merge (
557
+ self .matrix [edge .start ][edge .end ], edge )
558
+
559
+ def __str__ (self ):
560
+ return '\n ' .join ([' ' .join (map (str , row [1 :])) for row in self .matrix [1 :]])
561
+
562
+ def __call__ (self , u : int , v : int ):
563
+ return self .matrix [u ][v ]
564
+
565
+ def __iter__ (self ):
566
+ return self .matrix .__iter__ ()
0 commit comments