-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrefined_colouring.py
More file actions
459 lines (388 loc) · 13.4 KB
/
refined_colouring.py
File metadata and controls
459 lines (388 loc) · 13.4 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
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
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
from graph import *
from queue import Queue
"""
Fast colour refinement implementation.
Based on the algorithm ideas of Lecture 2 & 3.
"""
# ------- Start of Data structures -------
pointer = [] # List of pointers for each vertex.
dll = [] # List of DLL objects.
nx = [] # List of neighbours for each vertex.
COLOUR = [] # List of colours for each vertex.
ourQueue = Queue() # Working queue.
# ------- End of Data structures -------
class DLLEntry(object):
"""
A dynamic-linked-list entry object is used for building and working with the dynamic-linked-list object.
Thus, it is required to keep track of the 'left' and 'right' elements next to this entry.
This object is a representation of a 'state'.
"""
def __init__(self, colour, state, vertex):
"""
Constructor of a DLLEntry object.
:param colour: The colour of the state.
:param state: The integer identifier of the state.
:param vertex: The Vertex object associated with this state.
"""
self._colour = colour
self._state = state
self._vertex = vertex
self._left = None
self._right = None
def set_state(self, state):
"""
Basic setter for value of the state.
:param state: The new state value.
"""
self._state = state
def set_left(self, left):
"""
Basic setter for the pointer to the left element.
:param left: The pointer to the left element.
"""
self._left = left
def set_right(self, right):
"""
Basic setter for the pointer to the right element.
:param right: The pointer to the right element.
"""
self._right = right
@property
def vertex(self):
"""
Vertex property.
:return: The Vertex object associated with this entry.
"""
return self._vertex
@property
def colour(self):
"""
Colour property.
:return: The colour of this entry.
"""
return self._colour
@property
def state(self):
"""
State property.
:return: The integer identifier of this entry.
"""
return self._state
@property
def left(self):
"""
Left element property.
:return: A pointer to the entry found to the left of this one.
"""
return self._left
@property
def right(self):
"""
Right element property.
:return: A pointer to the entry found to the right of this one.
"""
return self._right
class DLL(object):
"""
The dynamic-linked-list object manages several DLLEntry objects.
The DLL object is useful for faster insertion, deletion and moving of elements.
At any point in time: start.left = end AND end.right = start.
"""
def __init__(self):
"""
Constructor of a DLL object.
"""
self._start = None
self._end = None
self._colour = None
self._size = 0
@property
def size(self):
"""
Size property.
:return: The number of DLLEntry objects found in this list.
"""
return self._size
@property
def start(self):
"""
Start property.
:return: A pointer to the first element in the list.
"""
return self._start
@property
def end(self):
"""
End property.
:return: A pointer to the last element in the list.
"""
return self._end
@property
def colour(self):
"""
Colour property.
:return: The colour of this object. This colour is equal to the colour of all DLLEntry objects in this list.
"""
return self._colour
def set_colour(self, colour):
"""
Basic setter for the colour of the object.
:param colour: The new colour.
"""
self._colour = colour
def set_start(self, start):
"""
Basic setter for the start element of the list.
:param start: The pointer to the new starting element.
"""
self._start = start
def set_end(self, end):
"""
Basic setter for the end element of the list.
:param end: The pointer to the new ending element.
"""
self._end = end
def set_size(self, size):
"""
Basic setter for the size of the object.
:param size: The new size of the object.
"""
self._size = size
def __repr__(self):
"""
String representation of this object.
:return:
"""
if self.start is None:
return ''
s = 'Colour ' + str(self.colour) +': '
p = self.start
while p != self.end:
s += str(p.state) + ' '
p = p.right
s += str(self.end.state) + '\n'
return s
def remove_state(self, vertex):
"""
Remove a DllEntry from this list.
This is done using 3 separate cases described bellow.
:param vertex: The Vertex to be removed.
"""
p = pointer[vertex.label]
if p is not None:
if self.size == 1: # If the size of the list is 1:
self.set_start(None) # Set all fields as none.
self.set_end(None)
self.set_size(0) # Set size as 0
elif self.size == 2: # If only two elements are in the list:
self.set_start(p.right) # Set start and end at the element not deleted
self.set_end(p.right)
p.left.set_right(p.right)
p.right.set_left(p.left)
self.set_size(1) # Set size to 1
else: # Multiple elements
p.left.set_right(p.right) # Change left / right relationships
p.right.set_left(p.left)
if self.start == p:
self.set_start(self.start.right)
if self.end == p:
self.set_end(self.end.left)
self.set_size(self.size - 1)
p = None # Delete entry
pointer[vertex.label] = None
return
def add_state(self, vertex):
"""
Add a new state to this list.
This is done using 3 separate cases described bellow.
:param vertex: The Vertex to be added.
"""
entry = DLLEntry(self.colour, vertex.label, vertex) # Create a new DLLEntry for this Vertex.
COLOUR[vertex.label] = self.colour # Keep track of the vertex's colour in a separate list.
if self.start is None: # If the list is empty:
entry.set_right(entry) # Set the left most and right most entry as this new one.
entry.set_left(entry)
self._start = entry
self._end = entry
elif self.end == self.start: # If there is only one element:
entry.set_left(self.start) # Set the left and right of this entry to the start.
entry.set_right(self.start)
self.start.set_left(entry)
self.start.set_right(entry)
self._end = entry # The end is now the new entry.
else: # If there are multiple entries:
entry.set_left(self.end) # Set left to end, right to start.
entry.set_right(self.start)
self.end.set_right(entry)
self.start.set_left(entry)
self._end = entry # The end is now the new entry.
self._size += 1 # Increment size of DLL
pointer[vertex.label] = entry
def get_states(self):
"""
Get this object's states.
:return: A list of Vertex objects which are found in this object.
"""
res = []
p = self.start
while p != self.end:
res.append(p)
p = p.right
res.append(self.end)
return res
def split_on_initial_colouring(dll, graph, initial_colouring):
"""
Arbitrarily split the vertices in different colour classes.
If no initial colouring is provided, the vertices will be split based on their degrees.
:param dll: An array of differently coloured DLL objects.
:param graph: The given graph.
:param initial_colouring: An array of colour such that the 'i-th' vertex has colour initial_colouring[i].
"""
if initial_colouring == []: # Check if any initial colouring was provided.
for i in graph.vertices: # Split the nodes on their degree.
degree = i.degree
dll[degree].add_state(i) # Add this vertex to the DLL with colour 'degree'.
else: # Split the nodes on initial colouring.
for i in range(len(initial_colouring)):
dll[initial_colouring[i]].add_state(graph.vertices[i])
def build_nx(graph):
"""
Build the neighbour list for each vertex.
:param graph: The given graph.
"""
for i in graph.vertices:
nx[i.label] = i.neighbours
def buildQueue(dll, ourQueue):
"""
Build the working queue.
:param dll: An array of differently coloured DLL objects.
:param ourQueue: An empty Queue object.
"""
aux = []
maxsize = 0
maxpointer = 0
k = 0
diffColours = 0
for l in dll:
if l.size > 0:
if maxsize < l.size:
maxsize = l.size
maxpointer = k # Keep track of the position of the dll with largest number of elements
k += 1
diffColours += 1
aux.append(l.colour) # Add each list to the queue
if diffColours > 1:
aux.pop(maxpointer) # Remove the list with largest number of elements.
for i in aux:
ourQueue.put(i)
def nicePrinting(dll):
"""
An attempt to print the list of DLL objects in a nice manner.
:param dll: An array of differently coloured DLL objects.
"""
for d in dll:
if d.size > 0:
print(d)
def colourGraph(dll, G):
"""
Colour the vertex of the graph by setting their 'colour' property.
This is intended for testing purposes only.
:param dll: An array of differently coloured DLL objects.
:param G: The given graph.
:return: The coloured graph.
"""
for d in dll:
if d.size > 0:
p = d.start
while p != d.end:
G.vertices[p.state].colornum = d.colour
p = p.right
G.vertices[d.end.state].colornum = d.colour
return G
def smallest_free_colour():
"""
Find the smallest free colour available.
:return: Such a colour if it was found. If not, create a new colour and return that one.
"""
for d in dll:
if d.size == 0 and d.colour != 0:
return d.colour
dll.append(DLL())
length = len(dll)
dll[length - 1].set_colour(length - 1)
return length - 1
def refine(C, G):
"""
Refine a colour class.
:param C: A DLL object representing a colour class.
:param G: The given graph.
"""
states = C.get_states() # Retrieve the states of this colour class.
L = []
visited = []
A = [0]*(len(G) + 1)
for q in states:
neighbours = nx[q.vertex.label]
for n in neighbours:
if COLOUR[n.label] != C.colour:
if COLOUR[n.label] not in L:
L.append(COLOUR[n.label])
visited.append(n)
A[COLOUR[n.label]] += 1
if n not in visited:
visited.append(n)
A[COLOUR[n.label]] += 1
for colour in L:
if A[colour] < dll[colour].size: # Split up the colour class.
l = smallest_free_colour() # Get the smallest free colour.
for v in visited: # Iterate over found neighbours.
if COLOUR[v.label] == colour: # Check that they belong to the respective colour class.
dll[colour].remove_state(v) # Remove the vertex from the old colour class.
dll[l].add_state(v) # Add vertex to the new colour class.
if dll[colour].size < dll[l].size:
ourQueue.put(colour)
else:
ourQueue.put(l)
def dllToList(dll, G):
"""
Transform the list of DLL objects to a an array of colours.
This is intended for testing purposes only.
:param dll: An array of differently coloured DLL objects.
:param G: The given graph.
:return: An array of colours representing the list of DLL objects.
"""
colors = [0] * (len(G.vertices))
for i in dll:
p = i.start
# print(p)
if i.size > 1:
while p != i.end:
colors[p.vertex.label] = i.colour
p = p.right
colors[p.vertex.label] = i.colour
elif i.size == 1:
colors[p.vertex.label] = i.colour
return colors
def refine_colour(G, initial_colouring):
global dll
global COLOUR
global pointer
global nx
dll = []
COLOUR = []
pointer = []
nx = []
for i in range(len(G)):
dll.append(DLL()) # Build the empty list of DLL objects.
dll[i].set_colour(i) # Set the colour of each DLL object.
COLOUR.append(-1) # Initialise the list of colours.
pointer.append(None) # Initialise the list of pointers.
nx.append(None) # Initialise the list of neighbours.
split_on_initial_colouring(dll, G, initial_colouring)
build_nx(G)
buildQueue(dll, ourQueue)
while not ourQueue.empty():
currentColour = ourQueue.get()
refine(dll[currentColour], G)
# colourGraph(dll, G)
return COLOUR, dll