11from .utils import *
22import random
3+ from typing import TypeVar , Callable
4+
5+
6+ __all__ = ["Edge" , "Graph" ]
37
48
59class Edge :
610 """Class Edge: A class of the edge in the graph"""
11+
712 def __init__ (self , u , v , w ):
813 """__init__(self, u, v, w) -> None
914 Initialize a edge.
@@ -26,11 +31,13 @@ def unweighted_edge(edge):
2631 """unweighted_edge(edge) -> str
2732 Return a string to output the edge without weight. The string contains the start vertex, end vertex(u,v) and splits with space.
2833 """
29- return '%d %d' % (edge .start ,edge .end )
34+ return '%d %d' % (edge .start , edge .end )
35+
3036
3137class Graph :
3238 """Class Graph: A class of the graph
3339 """
40+
3441 def __init__ (self , point_count , directed = False ):
3542 """__init__(self, point_count) -> None
3643 Initialize a graph.
@@ -49,6 +56,17 @@ def edge_count(self):
4956 cnt //= 2
5057 return cnt
5158
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+
5270 def to_str (self , ** kwargs ):
5371 """to_str(self, **kwargs) -> str
5472 Convert the graph to string with format. Splits with "\n "
@@ -66,7 +84,8 @@ def to_str(self, **kwargs):
6684 edge_buf = []
6785 for edge in self .iterate_edges ():
6886 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 ))
7089 random .shuffle (edge_buf )
7190 for edge in edge_buf :
7291 if not self .directed and random .randint (0 , 1 ) == 0 :
@@ -164,9 +183,10 @@ def tree(point_count, chain=0, flower=0, **kwargs):
164183 if not list_like (weight_limit ):
165184 weight_limit = (1 , weight_limit )
166185 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 ))
170190
171191 if not 0 <= chain <= 1 or not 0 <= flower <= 1 :
172192 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):
213233 if not list_like (weight_limit ):
214234 weight_limit = (1 , weight_limit )
215235 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 ]))
218238
219239 if not 0 <= left <= 1 or not 0 <= right <= 1 :
220240 raise Exception ("left and right must be between 0 and 1" )
221241 if left + right > 1 :
222242 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 ]
226246 graph = Graph (point_count , directed )
227247 for i in range (2 , point_count + 1 ):
228248 edge_pos = random .random ()
229249 node = 0
230250 # 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 )
233254 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
235257 if point_index < len (can_left ):
236258 # If the chosen element isn't the last one,
237259 # Copy the last one to the position of the chosen one
238260 can_left [point_index ] = del_last_node
239261 # Right
240262 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 )
243265 node = can_right [point_index ]
244266 del_last_node = can_right .pop ()
245267 if point_index < len (can_right ):
@@ -278,16 +300,17 @@ def graph(point_count, edge_count, **kwargs):
278300 if not list_like (weight_limit ):
279301 weight_limit = (1 , weight_limit )
280302 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 ]))
283305 graph = Graph (point_count , directed )
284306 used_edges = set ()
285307 i = 0
286308 while i < edge_count :
287309 u = random .randint (1 , point_count )
288310 v = random .randint (1 , point_count )
289311
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 ):
291314 # Then we generate a new pair of nodes
292315 continue
293316
@@ -318,9 +341,11 @@ def DAG(point_count, edge_count, **kwargs):
318341 -> the generator of the weights. It should return the weight. The default way is to use the random.randint()
319342 """
320343 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+ )
322347
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
324349 repeated_edges = kwargs .get ("repeated_edges" , True )
325350 loop = kwargs .get ("loop" , False )
326351 if not repeated_edges :
@@ -332,21 +357,22 @@ def DAG(point_count, edge_count, **kwargs):
332357 if not list_like (weight_limit ):
333358 weight_limit = (1 , weight_limit )
334359 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+
338363 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 ())
340366 graph = Graph (point_count , directed = True )
341367
342368 for edge in edge_buf :
343369 if loop and random .randint (1 , 2 ) == 1 :
344370 edge .start , edge .end = edge .end , edge .start
345371 graph .add_edge (edge .start , edge .end , weight = edge .weight )
346-
372+
347373 if not repeated_edges :
348374 used_edges .add ((edge .start , edge .end ))
349-
375+
350376 i = point_count - 1
351377 while i < edge_count :
352378 u = random .randint (1 , point_count )
@@ -355,7 +381,8 @@ def DAG(point_count, edge_count, **kwargs):
355381 if not loop and u > v :
356382 u , v = v , u
357383
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 ):
359386 # Then we generate a new pair of nodes
360387 continue
361388
@@ -383,8 +410,10 @@ def UDAG(point_count, edge_count, **kwargs):
383410 = lambda: random.randint(weight_limit[0], weight_limit[1])
384411 -> the generator of the weights. It should return the weight. The default way is to use the random.randint()
385412 """
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+ )
388417
389418 self_loop = kwargs .get ("self_loop" , True )
390419 repeated_edges = kwargs .get ("repeated_edges" , True )
@@ -397,23 +426,24 @@ def UDAG(point_count, edge_count, **kwargs):
397426 if not list_like (weight_limit ):
398427 weight_limit = (1 , weight_limit )
399428 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+
403432 used_edges = set ()
404433 graph = Graph .tree (point_count , weight_gen = weight_gen , directed = False )
405434
406435 for edge in graph .iterate_edges ():
407436 if not repeated_edges :
408437 used_edges .add ((edge .start , edge .end ))
409438 used_edges .add ((edge .end , edge .start ))
410-
439+
411440 i = point_count - 1
412441 while i < edge_count :
413442 u = random .randint (1 , point_count )
414443 v = random .randint (1 , point_count )
415444
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 ):
417447 # Then we generate a new pair of nodes
418448 continue
419449
@@ -459,8 +489,8 @@ def hack_spfa(point_count, **kwargs):
459489 if not list_like (weight_limit ):
460490 weight_limit = (1 , weight_limit )
461491 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 ]))
464494
465495 point_to_skip = point_count + 3
466496 graph = Graph (point_count , directed )
@@ -470,15 +500,18 @@ def hack_spfa(point_count, **kwargs):
470500
471501 for i in range (1 , half ):
472502 (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 ())
475506 (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 ())
478510 for i in range (1 , half + 1 ):
479511 (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 ())
482515
483516 for i in range (extraedg ):
484517 u = random .randint (1 , point_count )
@@ -495,3 +528,39 @@ def _calc_max_edge(point_count, directed, self_loop):
495528 if self_loop :
496529 max_edge += point_count
497530 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