Skip to content

Commit 1b2cfa6

Browse files
authored
Merge pull request #4 from rsarai/aco
Adds ACO
2 parents 3cfb8ae + 2d664e4 commit 1b2cfa6

16 files changed

+463
-1
lines changed

ACO Convergence Average 30 Runs.png

28.4 KB
Loading

ACO Found Solution.png

31.8 KB
Loading

Boxplot ACO Average 30 Runs.png

22.3 KB
Loading

Boxplot MMACO Average 30 Runs.png

27.8 KB
Loading

MMACO Convergence Average 30 Runs.png

30.3 KB
Loading

MMACO Found Solution.png

33.2 KB
Loading

Optimal Solution.png

28.7 KB
Loading

abcolony.py

-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ def scount_bees_exploration(self):
111111
probability_distribution = self.calculate_probabilities_for_onlookers(employer_bees)
112112

113113
for bee in self.colony:
114-
assert len(bee.current_position) == self.dimensions
115114
if bee.failures > self.fail_limit:
116115
bee.current_position = self._get_random_positions()
117116
onlookers = [b for b in self.colony if bee.food_source == b.food_source]

aco.py

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""
2+
The TSPLIB Symmetric Traveling Salesman Problem Instances
3+
http://elib.zib.de/pub/mp-testdata/tsp/tsplib/tsp/
4+
https://people.sc.fsu.edu/~jburkardt/datasets/tsp/tsp.html
5+
"""
6+
import random
7+
import math
8+
import numpy as np
9+
10+
from particle import Ant
11+
12+
# np.random.seed(42)
13+
# random.seed(42)
14+
15+
class Graph:
16+
17+
def __init__(self, cities):
18+
self.cities = cities
19+
self.pheromones = [[0.0 for _ in cities] for _ in cities]
20+
self.distance_matrix = [[]]
21+
self._create_distance_matrix()
22+
23+
def _create_distance_matrix(self):
24+
self.distance_matrix = []
25+
for city in self.cities:
26+
row = []
27+
for other_city in self.cities:
28+
row.append(np.linalg.norm(np.array(city) - np.array(other_city)))
29+
self.distance_matrix.append(row)
30+
31+
class ACO:
32+
33+
def __init__(self, graph, algorithm_type=1, rho_evaporation_rate=0.85, tour_counter=1000,
34+
ants_count=30, alpha=1, beta=5, t_max=0.8, t_min=0.2):
35+
"""
36+
:alpha: relative importance of pheromone
37+
:beta: relative importance of heuristic information
38+
:algorithm_type: 1 for regular ACO | 2 for MMACO
39+
"""
40+
self.graph = graph
41+
self.town_count = len(graph.cities)
42+
self.tour_counter = tour_counter
43+
self.ants_count = ants_count
44+
self.alpha = alpha
45+
self.beta = beta
46+
self.rho_evaporation_rate = rho_evaporation_rate
47+
self.t_max = t_max
48+
self.t_min = t_min
49+
self.algorithm_type = algorithm_type
50+
51+
self.best_solution = []
52+
self.best_cost = float("inf")
53+
self.best_cost_list = []
54+
self.all_ants = []
55+
56+
if self.algorithm_type != 1:
57+
self.graph.pheromones = [[self.t_max for _ in self.graph.cities] for _ in self.graph.cities]
58+
59+
def initialize_all_ants(self):
60+
self.all_ants = []
61+
for _ in range(self.ants_count):
62+
self.all_ants.append(
63+
Ant(current_town=random.randint(0, self.town_count - 1))
64+
)
65+
66+
def construct_tour_for_each_ant(self):
67+
for _ in range(self.town_count - 1):
68+
for ant in self.all_ants:
69+
old_town = ant.current_town
70+
ant.move_next_city_to_construct_tour(self.graph, self.alpha, self.beta)
71+
assert old_town != ant.current_town
72+
73+
def update_pheromone_trails(self):
74+
for ant in self.all_ants:
75+
ant.update_pheromone_trails(self.graph)
76+
77+
for i, row in enumerate(self.graph.pheromones):
78+
for j, _ in enumerate(row):
79+
self.graph.pheromones[i][j] *= (1 - self.rho_evaporation_rate)
80+
81+
if self.algorithm_type == 1:
82+
for ant in self.all_ants:
83+
self.graph.pheromones[i][j] += ant.pheromones_delta[i][j]
84+
else:
85+
ant = self.get_best_ant_from_iteration()
86+
self.graph.pheromones[i][j] += ant.pheromones_delta[i][j]
87+
if self.graph.pheromones[i][j] > self.t_max:
88+
self.graph.pheromones[i][j] = self.t_max
89+
if self.graph.pheromones[i][j] < self.t_min:
90+
self.graph.pheromones[i][j] = self.t_min
91+
92+
def get_best_ant_from_iteration(self):
93+
best_ant = None
94+
best_distance = float('inf')
95+
for ant in self.all_ants:
96+
distance = ant.total_cost
97+
# distance = self.calculate_tour_distance(self.graph, ant.tabu_list)
98+
# assert distance == ant.total_cost
99+
if distance < best_distance:
100+
best_ant = ant
101+
return best_ant
102+
103+
def update_best_solution(self):
104+
for ant in self.all_ants:
105+
ant.complet_tour_cost(self.graph)
106+
distance = ant.total_cost
107+
# distance = self.calculate_tour_distance(self.graph, ant.tabu_list)
108+
# assert round(distance, 5) == round(ant.total_cost, 5)
109+
if distance < self.best_cost:
110+
self.best_cost = distance
111+
self.best_solution = ant.tabu_list
112+
113+
def search(self):
114+
for i in range(self.tour_counter):
115+
self.initialize_all_ants()
116+
self.construct_tour_for_each_ant()
117+
self.update_pheromone_trails()
118+
self.update_best_solution()
119+
print(f"Iteration {i}. Best cost: {self.best_cost}")
120+
self.best_cost_list.append(self.best_cost)
121+
return self.best_cost_list
122+
123+
@classmethod
124+
def calculate_tour_distance(cls, graph, best_solution):
125+
result = 0
126+
for i in range(len(best_solution)):
127+
# result += math.sqrt((graph.cities[best_solution[i]][0] - graph.cities[best_solution[i - 1]][0])**2 + (graph.cities[best_solution[i]][1] - graph.cities[best_solution[i - 1]][1])**2)
128+
result += np.linalg.norm(np.array(graph.cities[best_solution[i]]) - np.array(graph.cities[best_solution[i - 1]]))
129+
return result

data/att48.txt

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
1 6734 1453
2+
2 2233 10
3+
3 5530 1424
4+
4 401 841
5+
5 3082 1644
6+
6 7608 4458
7+
7 7573 3716
8+
8 7265 1268
9+
9 6898 1885
10+
10 1112 2049
11+
11 5468 2606
12+
12 5989 2873
13+
13 4706 2674
14+
14 4612 2035
15+
15 6347 2683
16+
16 6107 669
17+
17 7611 5184
18+
18 7462 3590
19+
19 7732 4723
20+
20 5900 3561
21+
21 4483 3369
22+
22 6101 1110
23+
23 5199 2182
24+
24 1633 2809
25+
25 4307 2322
26+
26 675 1006
27+
27 7555 4819
28+
28 7541 3981
29+
29 3177 756
30+
30 7352 4506
31+
31 7545 2801
32+
32 3245 3305
33+
33 6426 3173
34+
34 4608 1198
35+
35 23 2216
36+
36 7248 3779
37+
37 7762 4595
38+
38 7392 2244
39+
39 3484 2829
40+
40 6271 2135
41+
41 4985 140
42+
42 1916 1569
43+
43 7280 4899
44+
44 7509 3239
45+
45 10 2676
46+
46 6807 2993
47+
47 5185 3258
48+
48 3023 1942

data/att48_optimal_tour.txt

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
1
2+
8
3+
38
4+
31
5+
44
6+
18
7+
7
8+
28
9+
6
10+
37
11+
19
12+
27
13+
17
14+
43
15+
30
16+
36
17+
46
18+
33
19+
20
20+
47
21+
21
22+
32
23+
39
24+
48
25+
5
26+
42
27+
24
28+
10
29+
45
30+
35
31+
4
32+
26
33+
2
34+
29
35+
34
36+
41
37+
16
38+
22
39+
3
40+
23
41+
14
42+
25
43+
13
44+
11
45+
12
46+
15
47+
40
48+
9

data/bays29.txt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
1 1150.0 1760.0
2+
2 630.0 1660.0
3+
3 40.0 2090.0
4+
4 750.0 1100.0
5+
5 750.0 2030.0
6+
6 1030.0 2070.0
7+
7 1650.0 650.0
8+
8 1490.0 1630.0
9+
9 790.0 2260.0
10+
10 710.0 1310.0
11+
11 840.0 550.0
12+
12 1170.0 2300.0
13+
13 970.0 1340.0
14+
14 510.0 700.0
15+
15 750.0 900.0
16+
16 1280.0 1200.0
17+
17 230.0 590.0
18+
18 460.0 860.0
19+
19 1040.0 950.0
20+
20 590.0 1390.0
21+
21 830.0 1770.0
22+
22 490.0 500.0
23+
23 1840.0 1240.0
24+
24 1260.0 1500.0
25+
25 1280.0 790.0
26+
26 490.0 2130.0
27+
27 1460.0 1420.0
28+
28 1260.0 1910.0
29+
29 360.0 1980.0

data/bays29_optimal_tour.txt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
1
2+
28
3+
6
4+
12
5+
9
6+
5
7+
26
8+
29
9+
3
10+
2
11+
20
12+
10
13+
4
14+
15
15+
18
16+
17
17+
14
18+
22
19+
11
20+
19
21+
25
22+
7
23+
23
24+
27
25+
8
26+
24
27+
16
28+
13
29+
21

main_aco.py

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import numpy as np
2+
import math
3+
from aco import ACO, Graph
4+
from plot_graphs import plot_path, plot_boxplot, plot_convergence_graphs
5+
6+
7+
SIMULATIONS = 30
8+
9+
10+
with open("data/att48.txt", "r") as f:
11+
matrix_cities = []
12+
for line in f.readlines():
13+
_, pos1, pos2 = line.split(" ")
14+
matrix_cities.append([float(pos1), float(pos2)])
15+
16+
with open("data/att48_optimal_tour.txt", "r") as f:
17+
best_solution = [int(line) for line in f.readlines()]
18+
best_solution = [i - 1 for i in best_solution]
19+
20+
graph = Graph(cities=matrix_cities)
21+
optimal = ACO.calculate_tour_distance(graph, best_solution)
22+
23+
24+
best_fitness = []
25+
for _ in range(SIMULATIONS):
26+
aco = ACO(graph=graph, algorithm_type=1, rho_evaporation_rate=0.9)
27+
best_cost_list = aco.search()
28+
best_fitness.append(best_cost_list)
29+
print("Optimal: ", optimal)
30+
print("Found: ", ACO.calculate_tour_distance(graph, aco.best_solution))
31+
print("ACO Min: ", np.array(best_fitness).min())
32+
33+
# plot_boxplot(best_fitness, "Boxplot ACO Average 30 Runs")
34+
# average_best_fitness = np.sum(np.array(best_fitness), axis=0) / SIMULATIONS
35+
36+
# plot_path(graph.cities, aco.best_solution, "ACO Found Solution")
37+
# plot_convergence_graphs(average_best_fitness, optimal, "ACO Convergence Average 30 Runs")
38+
39+
40+
best_fitness = []
41+
for _ in range(SIMULATIONS):
42+
aco = ACO(graph=graph, algorithm_type=2, rho_evaporation_rate=0.9)
43+
best_cost_list = aco.search()
44+
best_fitness.append(best_cost_list)
45+
print("Optimal: ", optimal)
46+
print("Found: ", ACO.calculate_tour_distance(graph, aco.best_solution))
47+
print(aco.best_solution)
48+
print("MMACO Min: ", np.array(best_fitness).min())
49+
50+
51+
# plot_boxplot(best_fitness, "Boxplot MMACO Average 30 Runs")
52+
# average_best_fitness = np.sum(np.array(best_fitness), axis=0) / SIMULATIONS
53+
54+
# plot_path(graph.cities, aco.best_solution, "MMACO Found Solution")
55+
# plot_convergence_graphs(average_best_fitness, optimal, "MMACO Convergence Average 30 Runs")
56+
57+
58+
# total_distance = 0
59+
# for i in range(len(best_solution)):
60+
# total_distance += np.linalg.norm(
61+
# np.array(matrix_cities[best_solution[i]]) - np.array(matrix_cities[best_solution[i - 1]])
62+
# )
63+
# print(total_distance)
64+
65+
66+
# result = 0
67+
# result_2 = 0
68+
# for i in range(len(best_solution)):
69+
# # partial_result = math.sqrt((graph.cities[best_solution[i]][0] - graph.cities[best_solution[i - 1]][0])**2 + (graph.cities[best_solution[i]][1] - graph.cities[best_solution[i - 1]][1])**2)
70+
# partial_result = np.linalg.norm(np.array(graph.cities[best_solution[i]]) - np.array(graph.cities[best_solution[i - 1]]))
71+
# partial_result_2 = graph.distance_matrix[best_solution[i]][best_solution[i - 1]]
72+
# assert partial_result == partial_result_2
73+
# result += partial_result
74+
# result_2 += partial_result_2
75+
76+
# print(result)
77+
# print(result_2)

0 commit comments

Comments
 (0)