-
Notifications
You must be signed in to change notification settings - Fork 1
/
graph.py
167 lines (130 loc) · 4.54 KB
/
graph.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
from typing import List, Dict
class GraphError(Exception):
pass
class Graph(object):
def __init__(self):
self._vertices = {}
self._next_label = 0
def create_vertex(self, households=0, label=None) -> "Vertex":
v = Vertex(self, households, label)
self._vertices[v.label] = v
return v
def del_vertex(self, vertex: "Vertex"):
del self._vertices[vertex.label]
def get_vertex(self, label) -> "Vertex":
return self._vertices[label]
def next_label(self):
res = self._next_label
self._next_label += 1
return res
@property
def vertices(self) -> List["Vertex"]:
return [v for (k, v) in self._vertices.items()]
class Vertex(object):
def __init__(self, graph: Graph, households=0, label=None):
if label is None:
label = graph.next_label()
self._graph = graph
self._households = households
self._label = label
self._incidence = {}
def __repr__(self) -> str:
return 'Vertex("{}", {}, #{})'.format(self.label, self.households, len(self._incidence))
def __str__(self) -> str:
return str(self.label)
def create_edge(self, to: "Vertex", weight=1.0):
"""
Creates an edge between this vertex and the given vertex.
:param to: the other vertex
:param weight: the weight (or failure probability) of the edge
:return: the newly created edge
"""
edge = Edge(self, to, weight)
self._incidence[to] = edge
to._incidence[self] = edge
return edge
def get_edge(self, to: "Vertex"):
"""
Retrieves the edge from this vertex to the given vertex.
:param to: the other vertex
:return: the edge if it exists, `None` otherwise
"""
if to not in self._incidence:
return None
return self._incidence[to]
def remove_edge(self, edge: "Edge"):
"""
Removes the edge from this (and the other) vertex's incidence.
:param edge: the edge to remove
"""
other = edge.get_other(self)
del self._incidence[other]
del other._incidence[self]
def is_neighbour(self, other: "Vertex"):
"""
Checks whether the given vertex is a neighbour of this vertex.
:param other: the other vertex
:return: `true` if there exists an edge from this vertex to the other vertex
"""
return other in self._incidence
@property
def edges(self) -> List["Edge"]:
return [v for (k, v) in self._incidence.items()]
@property
def graph(self) -> "Graph":
return self._graph
@property
def households(self):
return self._households
@property
def incidence(self) -> Dict["Vertex", "Edge"]:
return self._incidence
@property
def label(self):
return self._label
@property
def neighbours(self):
return [k for (k, v) in self._incidence.items()]
class Edge(object):
def __init__(self, tail: Vertex, head: Vertex, weight=0.5):
"""
Creates an edge from a given tail pointing to a given head. In the case of an undirected graph, the order of
these two doesnt matter.
:param tail: the originating vertex
:param head: the target vertex
:param weight: the weight (or failure probability) of the vertex
"""
self._tail = tail
self._head = head
self._weight = weight
def __repr__(self) -> str:
return "({}, {})".format(str(self.head), str(self.tail))
def __str__(self) -> str:
return self.__repr__()
def is_incident(self, vertex: Vertex):
"""
Checks whether the given vertex is either the head or the tail of this edge.
:param vertex: the vertex to check
:return: `true` if the vertex is equal to either the head or the tail of this edge
"""
return vertex == self.head or vertex == self.tail
def get_other(self, vertex: Vertex):
"""
Given either the head or the tail, will return the tail or the head respectively
:param vertex: the initial vertex
:return:
"""
if vertex == self.head:
return self.tail
elif vertex == self.tail:
return self.head
raise GraphError('Given vertex is neither on the head or tail of this edge')
@property
def tail(self):
return self._tail
@property
def head(self):
return self._head
@property
def weight(self):
return self._weight