1- from .utils import *
2- from .vector import Vector
1+ import itertools
32import math
43import random
5- import itertools
6- from typing import * # type: ignore
4+ from typing import (Callable , Counter , Iterable , List , Optional , Sequence ,
5+ Tuple , TypeVar , Union , cast )
6+
7+ from .utils import *
78
89__all__ = ["Edge" , "Graph" , "SwitchGraph" ]
910
@@ -40,25 +41,29 @@ class SwitchGraph:
4041 """A graph which can switch edges quickly"""
4142
4243 directed : bool
43- __edges : Dict [Tuple [int , int ], int ]
44+ __edges : Counter [Tuple [int , int ]]
4445
4546 def get_edges (self ):
47+ """
48+ Return a list of edges in the graph.
49+ """
4650 ret : List [Tuple [int , int ]] = []
47- for k in self .__edges :
51+ for k , c in self .__edges . items () :
4852 if self .directed or k [0 ] <= k [1 ]:
49- ret .extend (itertools .repeat (k , self . __edges [ k ] ))
53+ ret .extend (itertools .repeat (k , c ))
5054 return sorted (ret )
5155
5256 def edge_count (self ):
57+ """
58+ Return the count of edges in the graph.
59+ """
5360 val = 0
54- for k in self .__edges :
61+ for k , c in self .__edges . items () :
5562 if k [0 ] <= k [1 ]:
56- val += self . __edges [ k ]
63+ val += c
5764 return val
5865
5966 def __insert (self , u : int , v : int ):
60- if (u , v ) not in self .__edges :
61- self .__edges [(u , v )] = 0
6267 self .__edges [(u , v )] += 1
6368
6469 def __remove (self , u : int , v : int ):
@@ -67,40 +72,47 @@ def __remove(self, u: int, v: int):
6772 self .__edges .pop ((u , v ))
6873
6974 def insert (self , u : int , v : int ):
70- """Add edge (u, v)"""
75+ """
76+ Add edge (u, v) to the graph.
77+ """
7178 self .__insert (u , v )
7279 if not self .directed and u != v :
73- self .__insert (v , u )
80+ self .__insert (v , u ) # pylint: disable=W1114
7481
7582 def remove (self , u : int , v : int ):
76- """Remove edge (u, v)"""
83+ """
84+ Remove edge (u, v) from the graph.
85+ """
7786 self .__remove (u , v )
7887 if not self .directed and u != v :
79- self .__remove (v , u )
88+ self .__remove (v , u ) # pylint: disable=W1114
8089
81- def __init__ (
82- self , E : Iterable [Union [Edge , Tuple [int , int ]]], directed : bool = True
83- ):
90+ def __init__ (self ,
91+ edges : Iterable [Union [Edge , Tuple [int , int ]]],
92+ directed : bool = True ):
8493 self .directed = directed
85- self .__edges = {}
86- for e in E :
94+ self .__edges = Counter ()
95+ for e in edges :
8796 if isinstance (e , Edge ):
8897 self .insert (e .start , e .end )
8998 else :
9099 self .insert (e [0 ], e [1 ])
91100
92101 def switch (self , * , self_loop : bool = False , repeated_edges : bool = False ):
93- """Mutates the current directed graph by swapping pairs of edges, without impacting the degree sequence.
102+ """
103+ Mutates the current directed graph by swapping pairs of edges,
104+ without impacting the degree sequence.
94105
95- A switch is a general term for a small change in the structure of a graph, achieved by swapping small numbers
96- of edges.
106+ A switch is a general term for a small change in the structure of a graph,
107+ achieved by swapping small numbers of edges.
97108
98109 Returns:
99- If a switch was performed, then return True. If the switch was rejected, then return False.
110+ If a switch was performed, then return True.
111+ If the switch was rejected, then return False.
100112 """
101- first , second = random .choices (
102- list ( self . __edges . keys ()), list (self .__edges .values ()), k = 2
103- )
113+ first , second = random .choices (list ( self . __edges . keys ()),
114+ list (self .__edges .values ()),
115+ k = 2 )
104116 x1 , y1 = first if self .directed else sorted (first )
105117 x2 , y2 = second if self .directed else sorted (second )
106118
@@ -123,14 +135,19 @@ def switch(self, *, self_loop: bool = False, repeated_edges: bool = False):
123135 return True
124136
125137 @staticmethod
126- def from_directed_degree_sequence (
127- degree_sequence : Sequence [Tuple [int , int ]],
128- start_id : int = 1 ,
129- * ,
130- self_loop : bool = False ,
131- repeated_edges : bool = False
132- ):
133- """Generate a directed graph greedily based on the degree sequence."""
138+ def from_directed_degree_sequence (degree_sequence : Sequence [Tuple [int ,
139+ int ]],
140+ * ,
141+ self_loop : bool = False ,
142+ repeated_edges : bool = False ):
143+ """
144+ Generate a directed graph greedily based on the degree sequence.
145+
146+ Args:
147+ degree_sequence: The degree sequence of the graph.
148+ self_loop: Whether to allow self loops or not.
149+ repeated_edges: Whether to allow repeated edges or not.
150+ """
134151 if any (x < 0 or y < 0 for (x , y ) in degree_sequence ):
135152 raise ValueError ("Degree sequence is not graphical." )
136153
@@ -143,9 +160,8 @@ def from_directed_degree_sequence(
143160 if len (degree_sequence ) == 0 :
144161 return ret
145162
146- degseq = [
147- [sout , sin , vn ] for vn , (sin , sout ) in enumerate (degree_sequence , start_id )
148- ]
163+ degseq = [[sout , sin , vn ]
164+ for vn , (sin , sout ) in enumerate (degree_sequence , 1 )]
149165 degseq .sort (reverse = True )
150166
151167 try :
@@ -167,20 +183,24 @@ def from_directed_degree_sequence(
167183 break
168184 j += 1
169185 degseq .sort (reverse = True )
170- except IndexError :
171- raise ValueError ("Degree sequence is not graphical." )
186+ except IndexError as e :
187+ raise ValueError ("Degree sequence is not graphical." ) from e
172188
173189 return ret
174190
175191 @staticmethod
176- def from_undirected_degree_sequence (
177- degree_sequence : Sequence [int ],
178- start_id : int = 1 ,
179- * ,
180- self_loop : bool = False ,
181- repeated_edges : bool = False
182- ):
183- """Generate an undirected graph greedily based on the degree sequence."""
192+ def from_undirected_degree_sequence (degree_sequence : Sequence [int ],
193+ * ,
194+ self_loop : bool = False ,
195+ repeated_edges : bool = False ):
196+ """
197+ Generate an undirected graph greedily based on the degree sequence.
198+
199+ Args:
200+ degree_sequence: The degree sequence of the graph.
201+ self_loop: Whether to allow self loops or not.
202+ repeated_edges: Whether to allow repeated edges or not.
203+ """
184204 if any (x < 0 for x in degree_sequence ):
185205 raise ValueError ("Degree sequence is not graphical." )
186206
@@ -190,7 +210,7 @@ def from_undirected_degree_sequence(
190210 if len (degree_sequence ) == 0 :
191211 return SwitchGraph ((), False )
192212
193- degseq = [[deg , i ] for i , deg in enumerate (degree_sequence , start_id )]
213+ degseq = [[deg , i ] for i , deg in enumerate (degree_sequence , 1 )]
194214 degseq .sort (reverse = True )
195215
196216 edges : List [Tuple [int , int ]] = []
@@ -214,13 +234,13 @@ def from_undirected_degree_sequence(
214234 break
215235 y += 1
216236 degseq .sort (reverse = True )
217- except IndexError :
218- raise ValueError ("Degree sequence is not graphical." )
237+ except IndexError as e :
238+ raise ValueError ("Degree sequence is not graphical." ) from e
219239
220240 return SwitchGraph (edges , False )
221241
222242 def __iter__ (self ):
223- return iter ( self .__edges )
243+ return self .__edges . elements ( )
224244
225245
226246class Graph :
@@ -513,16 +533,16 @@ def graph(point_count, edge_count, **kwargs):
513533 return graph
514534
515535 @staticmethod
516- def from_degree_sequence (
517- degree_sequence : Union [ Sequence [ Tuple [ int , int ]], Sequence [int ]],
518- n_iter : Optional [int ] = None ,
519- * ,
520- self_loop : bool = False ,
521- repeated_edges : bool = False ,
522- weight_limit : Union [int , Tuple [int , int ]] = ( 1 , 1 ) ,
523- weight_gen : Optional [ Callable [[], int ]] = None ,
524- iter_limit : int = int ( 1e6 )
525- ):
536+ def from_degree_sequence (degree_sequence : Union [ Sequence [ Tuple [ int , int ]],
537+ Sequence [int ]],
538+ n_iter : Optional [int ] = None ,
539+ * ,
540+ self_loop : bool = False ,
541+ repeated_edges : bool = False ,
542+ weight_limit : Union [int , Tuple [int ,
543+ int ]] = ( 1 , 1 ) ,
544+ weight_gen : Optional [ Callable [[], int ]] = None ,
545+ iter_limit : int = int ( 1e6 ) ):
526546 if len (degree_sequence ) == 0 :
527547 return Graph (0 )
528548 if isinstance (weight_limit , int ):
@@ -553,9 +573,7 @@ def from_degree_sequence(
553573 directed ,
554574 self_loop ,
555575 repeated_edges ,
556- )
557- / math .log (edge_cnt )
558- )
576+ ) / math .log (edge_cnt ))
559577 n_iter = min (n_iter , iter_limit )
560578 for _ in range (n_iter ):
561579 sg .switch (self_loop = self_loop , repeated_edges = repeated_edges )
@@ -776,7 +794,8 @@ def _calc_max_edge(point_count, directed, self_loop):
776794 @staticmethod
777795 def _estimate_comb (n : int , k : int ):
778796 try :
779- return float (sum (math .log (n - i ) - math .log (i + 1 ) for i in range (k )))
797+ return float (
798+ sum (math .log (n - i ) - math .log (i + 1 ) for i in range (k )))
780799 except ValueError :
781800 return 0.0
782801
0 commit comments