-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgraph_adj_list.py
392 lines (353 loc) · 14 KB
/
graph_adj_list.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
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
#!/usr/bin/env python
from typing import Any, Union
from bridges.sl_element import *
from bridges.edge import *
import traceback
##
#
# @brief The GraphAdjList class can be used to represent adjacency list based
# graphs in BRIDGES
#
# The GraphAdjList class can be used to represent adjacency list based graphs
# in BRIDGES
#
# Convenience methods are provided to add vertices and edges to the graph as well as
# retrieve the adjacency list of a vertex, given its id.
#
# @author Matthew Mcquaigue
# @date 2018, 7/23/19
#
# \sa graph adjacency list tutorial, http://bridgesuncc.github.io/tutorials/Graph_AL.html
#
class GraphAdjList:
LargeGraphVertSize = 1000
force_large_viz = False
force_small_viz = False
def __init__(self) -> None:
"""
Constructor for a graph adj list
Returns:
None
"""
self._vertices = dict()
self.adj_list = dict()
def get_data_structure_type(self) -> str:
"""
Getter for the data structure type
Returns:
str: representing the type
"""
if (GraphAdjList.force_large_viz == True or (GraphAdjList.force_small_viz == False and
self.LargeGraphVertSize < len(self.vertices) and
self.are_all_vertices_located())):
return "largegraph"
return "GraphAdjacencyList"
def add_vertex(self, id: str,
label: str = None,
data: Any = None,
color: Union[str, Color] = "blue",
opacity: float = 1.0,
original: Element = None
) -> Element:
"""
Adds a new vertex to the graph, initializes the adjacency
list; user is responsible for checking if the vertex already exists.
This method will replace the value for this key
Args:
id: the vertex id
label: vertex label
data: the vertex data
color: color of vertex
opacity: opacity of vertex
original: an existing Element instance to copy, this will nullify other arguments
Returns:
newly created Vertex
"""
# note: it is the user's responsibility to check
# for duplicate vertices
self.vertices[id] = Element(val=data, color=color, opacity=opacity, original=original)
self.vertices.get(id).label = str(label) if label is not None else str(id)
self.adj_list[id] = None
return self.vertices[id]
def add_edge(self,
src: Union[str, Element],
dest: Union[str, Element],
label: str = None,
data: any = None,
color: Union[str, Color] = "blue",
thickness: float = 1.0,
opacity: float = 1.0
) -> Edge:
"""
Adds a new edge to the graph, adds it to that vertex's
adjacency list; user is responsible for checking if the
vertex already exists. This version assumes a default edge
weight of 1.
Args:
src: source vertex of edge
dest: destination vertex of edge
label: label of edge,
color: color of edge,
thickness: thickness of edge,
opacity: opacity of edge,
data: data the edge will hold
Returns:
newly created Edge
Rasies:
ValueError: if the src and dest vertices do not exist
"""
source_id = self._resolve_id(src)
dest_id = self._resolve_id(dest)
if source_id is None or dest_id is None:
raise ValueError("Vertex " + src + " or " + dest +
" does not exist! Add the vertex before creating the edge.")
try:
if self.vertices.get(source_id) is None or self.vertices.get(dest_id) is None:
raise ValueError("Vertex " + src + " or " + dest +
" does not exist! Add the vertex before creating the edge.")
except Exception as e:
traceback.print_tb(e.__traceback__)
self.adj_list[source_id] = SLelement(e=Edge(source_id, dest_id, data), next=self.adj_list.get(src), label=label,
color=color, thickness=thickness, opacity=opacity)
return self.adj_list[source_id].value
def set_vertex_data(self, src, vertex_data) -> None:
"""
Set for the data at a given vertex
Args:
src: the source vertex
vertex_data: The data for the vertex
Returns:
None
Raises:
ValueError: if the source vertex doesnt exist
"""
try:
if self.vertices[src] is None:
raise ValueError("Vertex " + src + " does not exist!")
except Exception as e:
traceback.print_tb(e.__traceback__)
self.vertices[src].value = vertex_data
def get_vertex_data(self, src):
try:
if self.vertices[src] is None:
raise ValueError("Vertex " + src + " does not exist!")
except Exception as e:
traceback.print_tb(e.__traceback__)
return self.vertices[src].value
def set_edge_data(self, src, dest, edge_data):
try:
if self.vertices[src] is None or self.vertices[dest] is None:
raise ValueError("Vertex " + src + " or " + dest + " does not exist!")
except Exception as e:
traceback.print_tb(e.__traceback__)
sle = self.adj_list[src]
while sle is not None:
edge_dest = sle.value.tov
if edge_dest == dest:
if edge_data is not None:
sle.value.set_edge_data(edge_data)
return
else:
return sle.value.edge_data
sle = sle.next
if sle is None:
raise ValueError("VEdge from " + src + " to " + dest + "does not exist!")
@property
def vertices(self) -> dict:
"""
Getter for the graph nodes
Returns:
dict
"""
return self._vertices
def get_vertex(self, key):
"""
Getter for a specific vertex in the dictionary of vertices
Args;
key: The associated key for the vertex
Returns:
vertex
"""
return self.vertices.get(key)
def get_adjacency_list(self, vertex=None):
"""
Gets the adjacency list
Args:
vertex: input vertex
Returns:
list : adjacency list of this vertex
"""
if vertex is not None:
return self.adj_list.get(vertex)
else:
return self.adj_list
def key_set(self):
return self.vertices.keys()
def value_set(self):
return self.vertices.values()
def out_going_edge_set_of(self, k):
return SLelement.list_helper(start=self.get_adjacency_list(k))
def get_edge_data(self, src, dest):
sle = self.adj_list[src]
while sle is not None:
ed = sle.value
if ed.destination == dest:
return ed.get_edge_data()
sle = sle.next
def get_edge(self, src: Union[str, Element], dest: Union[str, Element]):
source_id = self._resolve_id(src)
dest_id = self._resolve_id(dest)
if source_id is None or dest_id is None:
raise ValueError("Vertex " + src + " or " + dest +
" does not exist! Add the vertex before creating the edge.")
sle = self.adj_list[source_id]
while sle is not None:
ed = sle.value
if ed.destination == dest_id:
return ed
sle = sle.next
def _resolve_id(self, key_or_element: Union[str, Element]) -> Union[str, None]:
if type(key_or_element) is str:
return key_or_element
else:
return [key for key, value in self.vertices.items() if value == key_or_element].pop()
def are_all_vertices_located(self):
for element in self.vertices.items():
el = element[1]
elvis = el.visualizer
if(elvis.location_x == float('inf') or elvis.location_y == float('inf')):
return False
return True
def force_large_visualization(self, f):
if f:
GraphAdjList.force_large_viz = True
GraphAdjList.force_small_viz = False
else:
GraphAdjList.force_large_viz = False
def force_small_visualization(self, f):
if f:
GraphAdjList.force_small_viz = True
GraphAdjList.force_large_viz = False
else:
GraphAdjList.force_small_viz = False
##
#
# This is a convenience method to simplify access to the link visualizer;
# the method assumes the vertex names point to existing vertices, else an exception
# is thrown
#
# @param src - source vertex of edge
# @param dest - destination vertex of edge
#
def get_link_visualizer(self, src, dest):
# get the source and destination vertex elements
# and check to see if they exist
v1 = self.vertices.get(src)
v2 = self.vertices.get(dest)
try:
if v1 is None or v2 is None:
raise ValueError(
"Vertex " + src + " or " + dest + " does not exist! First add the vertices to the graph.")
except Exception as e:
traceback.print_tb(e.__traceback__)
return v1.get_link_visualizer(v2)
##
#
# This is a convenience method to simplify access to the element visualizer;
# the method assumes the vertex name points to an existing vertice, else an
# exception is thrown
#
# @param vertex - The vertex for which visualizer is wanted
#
def get_visualizer(self, vertex):
# get the source and destination vertex elements
# and check to see if they exist
v = self.vertices.get(vertex)
try:
if v is None:
raise ValueError("Vertex " + vertex + " does not exist! First add the vertices to the graph.")
except Exception as e:
traceback.print_tb(e.__traceback__)
return v.visualizer
def get_data_structure_representation(self) -> dict:
"""
Get the representation of the data structure as a dict
Returns:
dict: representing the JSON format before dumping to server
"""
node_map = dict() # map to reorder the nodes for building JSON
nodes = [] # get the list nodes
nodes_JSON = [] # array for list of nodes in json
links_JSON = [] # array for building the links JSON - traverse the adj. lists
# redirect for large graphs
if (GraphAdjList.force_large_viz == True or (GraphAdjList.force_small_viz == False and
self.LargeGraphVertSize < len(self.vertices) and
self.are_all_vertices_located())):
return self.get_data_structure_large_graph()
# get the objects and add them to the array
for elements in self.vertices.items():
nodes.append(elements[1])
# append all nodes representation to list of nodes
for i in range(0, len(nodes)):
node_map[nodes[i]] = i
nodes_JSON.append(nodes[i].get_element_representation())
# get all nodes in adj_list
for a_list in self.adj_list.items():
links_list = a_list[1]
src_vert = self.vertices.get(a_list[0])
# get the source vertex index for the JSON (int)
while links_list is not None:
src_indx = node_map.get(src_vert)
# get the destination vertex index for the JSON (int)
edge = links_list.value
dest_vert = self.vertices.get(edge.tov)
dest_indx = node_map.get(dest_vert)
# get link representation
links_JSON.append((links_list.get_link_representation(
edge._lvis,
str(src_indx),
str(dest_indx))))
links_list = links_list.next
json_str = {
"nodes": nodes_JSON,
"links": links_JSON
}
return json_str
def get_data_structure_large_graph(self) -> dict:
nodes = []
node_map = dict()
for element in self.vertices.items():
nodes.append(element[1])
nodes_json = []
for k in range(0, len(nodes)):
nodes_json.append([])
node_map[nodes[k]] = k
elvis = nodes[k].visualizer
loc_str = []
if elvis.location_x != float('inf') and elvis.location_y != float('inf'):
loc_str.append(elvis.location_x)
loc_str.append(elvis.location_y)
nodes_json[k].append(loc_str)
color = elvis.color
nodes_json[k].append([color.red, color.green, color.blue, color.alpha])
links_json = []
for a_list in self.adj_list.items():
list = a_list[1]
src_vert = self.vertices.get(a_list[0])
while list is not None:
link_json=[]
src_indx = node_map.get(src_vert)
edge = list.value
dest_vert = self.vertices.get(edge.tov)
dest_indx = node_map.get(dest_vert)
color = src_vert.get_link_visualizer(dest_vert).color
link_json.append(src_indx)
link_json.append(dest_indx)
link_json.append([color.red, color.green, color.blue, color.alpha])
links_json.append(link_json)
list = list.next
graph_alist_json = {
"nodes": nodes_json,
"links": links_json
}
return graph_alist_json