-
Notifications
You must be signed in to change notification settings - Fork 2
/
ant.py
119 lines (90 loc) · 3.72 KB
/
ant.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
import math
from threading import *
class Ant(Thread):
def __init__(self, ID, start_node, colony, beta, alpha, Q0, Q, rho):
Thread.__init__(self)
self.ID = ID
self.start_node = start_node
self.colony = colony
self.curr_node = self.start_node
self.graph = self.colony.graph
self.path_vec = []
self.path_vec.append(self.start_node)
self.path_cost = 0
# same meaning as in standard equations
self.Beta = beta
self.Alpha = alpha
#self.Q0 = 1 # Q0 = 1 works just fine for 10 city case (no explore)
self.Q0 = Q0
self.Q = Q # pheromone constant value
self.Rho = rho
# store the nodes remaining to be explored here
self.nodes_to_visit = {}
for i in range(0, self.graph.num_nodes):
if i != self.start_node:
self.nodes_to_visit[i] = i
# create n X n matrix 0'd out to start
self.path_mat = []
for i in range(0, self.graph.num_nodes):
self.path_mat.append([0]*self.graph.num_nodes)
# override Thread's run()
def run(self):
graph = self.colony.graph
while not self.end():
# we need exclusive access to the graph
graph.lock.acquire()
new_node = self.state_transition_rule(self.curr_node)
self.path_cost += graph.delta(self.curr_node, new_node)
self.path_vec.append(new_node)
self.path_mat[self.curr_node][new_node] = 1 #adjacency matrix representing path
self.local_updating_rule(self.curr_node, new_node, self.path_cost)
graph.lock.release()
self.curr_node = new_node
# don't forget to close the tour
self.path_cost += graph.delta(self.path_vec[-1], self.path_vec[0])
# send our results to the colony
self.colony.update(self)
# allows thread to be restarted (calls Thread.__init__)
self.__init__(self.ID, self.start_node, self.colony, self.Beta, self.Alpha, self.Q0, self.Q, self.Rho)
def end(self):
return not self.nodes_to_visit
def key_with_max_val(self, d):
v = list(d.values())
k = list(d.keys())
return k[v.index(max(v))]
# determines next node to visit after curr_node
def state_transition_rule(self, curr_node):
graph = self.colony.graph
max_node = -1
max_val = -1
nominator = {}
for node in self.nodes_to_visit.values():
if graph.tau(curr_node, node) == 0:
raise Exception("tau = 0")
nominator[node] = math.pow(graph.tau(curr_node, node), self.Alpha) *\
math.pow(graph.etha(curr_node, node), self.Beta)
sum = 0
node = -1
for node in self.nodes_to_visit.values():
if graph.tau(curr_node, node) == 0:
raise Exception("tau = 0")
sum += math.pow(graph.tau(curr_node, node), self.Alpha) * math.pow(graph.etha(curr_node, node), self.Beta)
if sum == 0:
raise Exception("sum = 0")
p = {}
for node in self.nodes_to_visit.values():
p[node] = nominator[node] / sum
max_node = self.key_with_max_val(p)
if max_node == -1:
max_node = node
if max_node < 0:
raise Exception("max_node < 0")
del self.nodes_to_visit[max_node]
del p[max_node]
del nominator[max_node]
return max_node
# pheromone update rule for indiv ants
def local_updating_rule(self, curr_node, next_node, path_cost):
graph = self.colony.graph
val = self.Rho * graph.tau(curr_node, next_node) + (self.Q / path_cost)
graph.update_tau(curr_node, next_node, val)