-
Notifications
You must be signed in to change notification settings - Fork 49
/
ant.py
126 lines (96 loc) · 4 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
120
121
122
123
124
125
import math
import random
import sys
from threading import *
class Ant(Thread):
def __init__(self, ID, start_node, colony):
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 = 1
#self.Q0 = 1 # Q0 = 1 works just fine for 10 city case (no explore)
self.Q0 = 0.5
self.Rho = 0.99
# 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)
# overide 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
print ("Ant %s : %s, %s" % (self.ID, self.path_vec, self.path_cost,))
self.local_updating_rule(self.curr_node, new_node)
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)
print ("Ant thread %s terminating." % (self.ID,))
# allows thread to be restarted (calls Thread.__init__)
self.__init__(self.ID, self.start_node, self.colony)
def end(self):
return not self.nodes_to_visit
# described in report -- determines next node to visit after curr_node
def state_transition_rule(self, curr_node):
graph = self.colony.graph
q = random.random()
max_node = -1
if q < self.Q0:
print ("Exploitation")
max_val = -1
val = None
for node in self.nodes_to_visit.values():
if graph.tau(curr_node, node) == 0:
raise Exception("tau = 0")
val = graph.tau(curr_node, node) * math.pow(graph.etha(curr_node, node), self.Beta)
if val > max_val:
max_val = val
max_node = node
else:
print ("Exploration")
sum = 0
node = -1
for node in self.nodes_to_visit.values():
if graph.tau(curr_node, node) == 0:
raise Exception("tau = 0")
sum += graph.tau(curr_node, node) * math.pow(graph.etha(curr_node, node), self.Beta)
if sum == 0:
raise Exception("sum = 0")
avg = sum / len(self.nodes_to_visit)
print ("avg = %s" % (avg,))
for node in self.nodes_to_visit.values():
p = graph.tau(curr_node, node) * math.pow(graph.etha(curr_node, node), self.Beta)
if p > avg:
print ("p = %s" % (p,))
max_node = node
if max_node == -1:
max_node = node
if max_node < 0:
raise Exception("max_node < 0")
del self.nodes_to_visit[max_node]
return max_node
# phermone update rule for indiv ants
def local_updating_rule(self, curr_node, next_node):
graph = self.colony.graph
val = (1 - self.Rho) * graph.tau(curr_node, next_node) + (self.Rho * graph.tau0)
graph.update_tau(curr_node, next_node, val)