Skip to content

Commit 340c732

Browse files
Format code and docs
1 parent 639ad03 commit 340c732

File tree

1 file changed

+85
-66
lines changed

1 file changed

+85
-66
lines changed

cyaron/graph.py

Lines changed: 85 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from .utils import *
2-
from .vector import Vector
1+
import itertools
32
import math
43
import 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

226246
class 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

Comments
 (0)