-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase.py
More file actions
270 lines (236 loc) · 7.79 KB
/
base.py
File metadata and controls
270 lines (236 loc) · 7.79 KB
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
class Vertex:
"""
Vertex Class represents a vertex in graph
"""
#__slots__ = ['index', 'label', 'predecessor', 'status']
def __init__(self, label):
"""
Initialize vertex's attributes
index -- Self incremental integer storing its index in the whole graph
lable -- String name representing this vertex
Temporary attributes used in graph traversal
predecessor (Vertex) -- Vertex's predecessor
dtime (int) -- Discovered time in dfs
ftime (int) -- Finisheed time in dfs
cost (float) -- Cost to reach this vertex in Dijkstra
"""
self.index = -1
self.label = label
self.predecessor = None
self.dtime = -1
self.ftime = -1
self.cost = float('inf')
self.status = ()
def __getitem__(self, key):
"""
Protocal for dict style get
Eg. vertex['index']
"""
if key == 'index':
return self.index
elif key == 'label':
return self.label
elif key == 'predecessor':
return self.predecessor
elif key == 'dtime':
return self.dtime
elif key == 'ftime':
return self.ftime
def __setitem__(self, key, value):
"""
Protocol for dict style set
Eg. vertex['index'] = index
"""
if key == 'index':
self.index = value
elif key == 'label':
self.label = value
elif key == 'predecessor':
self.predecessor = value
elif key == 'dtime':
self.dtime= value
elif key == 'ftime':
self.ftime = value
def __hash__(self):
return hash(self.index)
def __eq__(self, other):
return self.index == other.index
def __repr__(self):
# return '%s_%s(%d)' % (self.__class__.__name__, self.label, self.index)
return self.label
def __str__(self):
return '%s[%f]' % (self.label, self.cost)
class Edge:
def __init__(self, source, destination, weight=1.0):
"""
Initialize Edge attributes
source (Vertex) -- The source vertex (one end) of the edge
destination (Vertex) -- The destination vertex (the other end) of the edge
weight (float) -- The weight of the edge, default is 1.0 in unweighted graph
"""
self.source = source
self.destination = destination
self.weight = weight
def __getitem__(self, key):
if key == 'source':
return self.source
elif key == 'dest':
return self.destination
elif key == 'weight':
return self.weight
elif key == 'type':
return self.type
def __setitem__(self, key, value):
if key == 'weight':
self.weight = value
elif key == 'type':
self.type = value
def reverse(self):
return Edge(self.destination, self.source, self.weight)
def __str__(self):
return '%s->%s [%d]' % (str(self.source), str(self.destination), self.weight)
class GraphAdj:
"""
Graph class using adjacent representation
It contains a list of vertices each of which has
a list edges representing its connection with other vertices
"""
def __init__(self, conf=None):
"""
Initialize
vertices (list) -- All the Vertex instances contained
curIndex (int) -- Current iterating vertex's index in the graph
"""
self.vertices = []
self.curIndex = -1
if conf is not None:
self.setup(conf)
def setup(self, conf):
"""
Set up the graph with a conf file
It has the following format:
a b c d e # the label of each vertex
0 1 21 # an edge between vertex 0 (a) and 1 (b) with weight as 21
0 2 55 # an edge between vertex 0 (a) and 2 (c) with weight as 55
...
"""
f = open(conf)
for i, line in enumerate(f):
items = line.split(' ')
if i == 0:
# the first line contains all vertex label, add them all
# as graph ventry instances
for item in items:
self.addVertex(Vertex( item.strip() ))
continue
# from the second line representing connection
sindex, dindex = items[0], items[1]
try:
weight = float(items[2])
except IndexError:
# no weight specified
weight = 1.0
self.addEdge(sindex, dindex, weight)
def output(self):
print "initial graph: \n",
for u in self:
for e in self.adjcent(u):
print e
class VEntry:
"""
Internal class representing a vertex and its edges
"""
def __init__(self, v):
self.vert = v
self.edges = []
# def __str__(self):
# return str(self.vert)
class EdgeIter:
"""
Internal class for iterator of a vertex's edges
"""
def __init__(self, v, graph):
self.graph = graph
self.edgesTo = graph.getVEntry(v).edges
def __iter__(self):
"""
Make edges iterable using for .. in syntax
"""
return iter(self.edgesTo)
def addVertex(self, v):
v['index'] = self.size()
self.vertices.append(GraphAdj.VEntry(v))
def addEdge(self, sindex, dindex, weight):
"""
Add an edge to link source to destination vertex
Only create Edge instance in the base class, since for
undirected and directed graph it has different ways to
add the edge in both connected vertices
@param sindex (int) -- The index of source vertex
@param dindex (int) -- The index of destination vertex
@param weight (float) -- The weight of the edge
@return Edge instance
"""
sindex, dindex = int(sindex), int(dindex)
try:
source = self.getVertex(sindex)
dest = self.getVertex(dindex)
newedge = Edge(source, dest, weight)
return newedge
except IndexError:
print "vertex indext error"
def getVertex(self, index):
try:
result = self.vertices[index].vert
except IndexError:
result = None
return result
def getVEntry(self, v):
return self.vertices[v['index']]
def size(self):
return len(self.vertices)
def degree(self, v):
"""
The number of outgoing edges of the vertex
@return int
"""
return len(self.getVEntry(v).edges)
"""
Protocol to make the graph iterable over its vertices list
using for ... in syntax
"""
def next(self):
self.curIndex += 1
try:
#result = self.getVertex(self.curIndex)
result = self.vertices[self.curIndex].vert
except IndexError:
self.curIndex = -1
raise StopIteration
return result
def __iter__(self):
#vlist = [each.vert for each in self.vertices]
#return iter(vlist)
return self
def adjcent(self, u):
"""
Visit the adjcent nodes of a vertex
@return iterator to loop the edge list of a vertex
"""
return self.EdgeIter(u, self)
def path(self, dest):
pre = dest['predecessor']
if pre is None:
return [dest]
return self.path(pre) + [dest]
if __name__ == '__main__':
G = GraphAdj()
for i in range(5):
G.addVertex( Vertex('a'+str(i)) )
#for u in G:
#print '...'
#v1 = Vertex('a')
#v2 = Vertex('b')
#print v1['label']
#print v2['label']
#v2['label'] = 'c'