forked from ppoffice/ant-colony-tsp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaco.py
121 lines (111 loc) · 4.95 KB
/
aco.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
import random
class Graph(object):
def __init__(self, cost_matrix: list, rank: int):
"""
:param cost_matrix:
:param rank: rank of the cost matrix
"""
self.matrix = cost_matrix
self.rank = rank
# noinspection PyUnusedLocal
self.pheromone = [[1 / (rank * rank) for j in range(rank)] for i in range(rank)]
class ACO(object):
def __init__(self, ant_count: int, generations: int, alpha: float, beta: float, rho: float, q: int,
strategy: int):
"""
:param ant_count:
:param generations:
:param alpha: relative importance of pheromone
:param beta: relative importance of heuristic information
:param rho: pheromone residual coefficient
:param q: pheromone intensity
:param strategy: pheromone update strategy. 0 - ant-cycle, 1 - ant-quality, 2 - ant-density
"""
self.Q = q
self.rho = rho
self.beta = beta
self.alpha = alpha
self.ant_count = ant_count
self.generations = generations
self.update_strategy = strategy
def _update_pheromone(self, graph: Graph, ants: list):
for i, row in enumerate(graph.pheromone):
for j, col in enumerate(row):
graph.pheromone[i][j] *= self.rho
for ant in ants:
graph.pheromone[i][j] += ant.pheromone_delta[i][j]
# noinspection PyProtectedMember
def solve(self, graph: Graph):
"""
:param graph:
"""
best_cost = float('inf')
best_solution = []
for gen in range(self.generations):
# noinspection PyUnusedLocal
ants = [_Ant(self, graph) for i in range(self.ant_count)]
for ant in ants:
for i in range(graph.rank - 1):
ant._select_next()
ant.total_cost += graph.matrix[ant.tabu[-1]][ant.tabu[0]]
if ant.total_cost < best_cost:
best_cost = ant.total_cost
best_solution = [] + ant.tabu
# update pheromone
ant._update_pheromone_delta()
self._update_pheromone(graph, ants)
# print('generation #{}, best cost: {}, path: {}'.format(gen, best_cost, best_solution))
return best_solution, best_cost
class _Ant(object):
def __init__(self, aco: ACO, graph: Graph):
self.colony = aco
self.graph = graph
self.total_cost = 0.0
self.tabu = [] # tabu list
self.pheromone_delta = [] # the local increase of pheromone
self.allowed = [i for i in range(graph.rank)] # nodes which are allowed for the next selection
self.eta = [[0 if i == j else 1 / graph.matrix[i][j] for j in range(graph.rank)] for i in
range(graph.rank)] # heuristic information
start = random.randint(0, graph.rank - 1) # start from any node
self.tabu.append(start)
self.current = start
self.allowed.remove(start)
def _select_next(self):
denominator = 0
for i in self.allowed:
denominator += self.graph.pheromone[self.current][i] ** self.colony.alpha * self.eta[self.current][
i] ** self.colony.beta
# noinspection PyUnusedLocal
probabilities = [0 for i in range(self.graph.rank)] # probabilities for moving to a node in the next step
for i in range(self.graph.rank):
try:
self.allowed.index(i) # test if allowed list contains i
probabilities[i] = self.graph.pheromone[self.current][i] ** self.colony.alpha * \
self.eta[self.current][i] ** self.colony.beta / denominator
except ValueError:
pass # do nothing
# select next node by probability roulette
selected = 0
rand = random.random()
for i, probability in enumerate(probabilities):
rand -= probability
if rand <= 0:
selected = i
break
self.allowed.remove(selected)
self.tabu.append(selected)
self.total_cost += self.graph.matrix[self.current][selected]
self.current = selected
# noinspection PyUnusedLocal
def _update_pheromone_delta(self):
self.pheromone_delta = [[0 for j in range(self.graph.rank)] for i in range(self.graph.rank)]
for _ in range(1, len(self.tabu)):
i = self.tabu[_ - 1]
j = self.tabu[_]
if self.colony.update_strategy == 1: # ant-quality system
self.pheromone_delta[i][j] = self.colony.Q
elif self.colony.update_strategy == 2: # ant-density system
# noinspection PyTypeChecker
self.pheromone_delta[i][j] = self.colony.Q / self.graph.matrix[i][j]
else: # ant-cycle system
self.pheromone_delta[i][j] = self.colony.Q / self.total_cost