-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
241 lines (199 loc) · 9.05 KB
/
Copy pathmain.py
File metadata and controls
241 lines (199 loc) · 9.05 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
"""
TSP Solver - Main Module
Entry point for solving TSP with different algorithms and modern visualizations.
This version runs in an interactive menu loop
Guarantees
----------
- All algorithms start from the SAME initial random tour (shared baseline).
- The dashed route in the animation is always that shared initial tour.
- Animations do NOT restart
- Final tours are printed for each algorithm.
"""
from __future__ import annotations
import random
from typing import List
from tsp_reader import read_tsp_file, calculate_distance_matrix
from hill_climbing import HillClimbing
from genetic_algorithm import GeneticAlgorithm
from ant_colony import AntColonyOptimization
from visualizer import TSPVisualizer
def _format_tour(tour: List[int]) -> str:
if not tour:
return ""
return " -> ".join(map(str, tour + [tour[0]]))
def _make_initial_tour(n: int, seed: int) -> List[int]:
rng = random.Random(seed)
tour = list(range(n))
rng.shuffle(tour)
return tour
def main() -> None:
tsp_filename = "berlin52.tsp"
user_seed = None
fullscreen = True
show_labels = False
print("=" * 70)
print("TSP SOLVER - ALGORITHM VISUALIZER")
print("=" * 70)
# Read TSP file once
print(f"\n[*] Reading: {tsp_filename}")
try:
coords = read_tsp_file(tsp_filename)
print(f"[*] Loaded {len(coords)} cities")
except FileNotFoundError:
print(f"[!] Error: TSP file '{tsp_filename}' not found. Put it next to main.py.")
return
# Distance matrix once
print("[*] Calculating distance matrix...")
dist_matrix = calculate_distance_matrix(coords)
print(f"[*] Distance matrix computed ({len(coords)}x{len(coords)})")
visualizer = TSPVisualizer(coords)
# Shared initial tour is kept for the whole session by default.
# (So if you run algorithms one-by-one, they remain comparable.)
seed = user_seed if user_seed is not None else random.randrange(1_000_000_000)
initial_tour = _make_initial_tour(len(coords), seed)
def print_initial_info() -> None:
initial_len = float(
sum(
dist_matrix[initial_tour[i], initial_tour[(i + 1) % len(initial_tour)]]
for i in range(len(initial_tour))
)
)
print(f"\n[*] Shared initial tour seed: {seed}")
print(f"[*] Shared initial distance: {initial_len:.2f}")
print_initial_info()
while True:
# Menu loop so closing the animation window does NOT end the program
print("\nSelect algorithm to visualize:")
print("1. Hill Climbing (Basic)")
print("2. Genetic Algorithm")
print("3. Ant Colony Optimization")
print("4. Run all algorithms (single comparison animation)")
print("5. New initial random tour (new seed)")
print("\n0. Exit")
choice = input("\nEnter your choice (0-5): ").strip()
if choice == "0":
print("Exiting...")
return
if choice == "5":
seed = random.randrange(1_000_000_000)
initial_tour = _make_initial_tour(len(coords), seed)
print_initial_info()
continue
try:
if choice == "1":
print("\n[*] Running Hill Climbing...")
hc = HillClimbing(dist_matrix, max_iterations=1500, seed=seed + 1)
hc_tour, hc_len = hc.solve(initial_tour=initial_tour, stagnation_patience=180)
print(f"[*] Hill Climbing final distance: {hc_len:.2f}")
print(f"[*] Hill Climbing final tour: {_format_tour(hc_tour)}")
print("\n[*] Creating animation...")
_anim = visualizer.animate_single_algorithm(
hc.history,
"Hill Climbing",
color="#FF6B6B",
interval=35,
initial_tour=initial_tour,
show_city_labels=show_labels,
fullscreen=fullscreen,
)
elif choice == "2":
print("\n[*] Running Genetic Algorithm...")
ga = GeneticAlgorithm(
dist_matrix,
population_size=120,
generations=700,
mutation_rate=0.03,
seed=seed + 2,
)
ga_tour, ga_len = ga.solve(initial_tour=initial_tour, stagnation_patience=120)
print(f"[*] Genetic Algorithm final distance: {ga_len:.2f}")
print(f"[*] Genetic Algorithm final tour: {_format_tour(ga_tour)}")
print("\n[*] Creating animation...")
_anim = visualizer.animate_single_algorithm(
ga.history,
"Genetic Algorithm",
color="#4ECDC4",
interval=35,
initial_tour=initial_tour,
show_city_labels=show_labels,
fullscreen=fullscreen,
)
elif choice == "3":
print("\n[*] Running Ant Colony Optimization...")
aco = AntColonyOptimization(
dist_matrix,
n_ants=35,
n_iterations=450,
alpha=1.0,
beta=2.5,
evaporation_rate=0.45,
Q=120.0,
seed=seed + 3,
)
aco_tour, aco_len = aco.solve(initial_tour=initial_tour, stagnation_patience=90)
print(f"[*] Ant Colony final distance: {aco_len:.2f}")
print(f"[*] Ant Colony final tour: {_format_tour(aco_tour)}")
print("\n[*] Creating animation...")
_anim = visualizer.animate_single_algorithm(
aco.history,
"Ant Colony Optimization",
color="#7C5CFC",
interval=35,
initial_tour=initial_tour,
show_city_labels=show_labels,
fullscreen=fullscreen,
)
elif choice == "4":
print("\n[*] Running all algorithms (same initial tour)...")
print("\n [1/3] Hill Climbing...")
hc = HillClimbing(dist_matrix, max_iterations=1500, seed=seed + 1)
hc_tour, hc_len = hc.solve(initial_tour=initial_tour, stagnation_patience=180)
print(f" - Distance: {hc_len:.2f} | steps: {len(hc.history) - 1}")
print("\n [2/3] Genetic Algorithm...")
ga = GeneticAlgorithm(dist_matrix, population_size=120, generations=700, mutation_rate=0.03, seed=seed + 2)
ga_tour, ga_len = ga.solve(initial_tour=initial_tour, stagnation_patience=120)
print(f" - Distance: {ga_len:.2f} | steps: {len(ga.history) - 1}")
print("\n [3/3] Ant Colony Optimization...")
aco = AntColonyOptimization(dist_matrix, n_ants=35, n_iterations=450, alpha=1.0, beta=2.5, evaporation_rate=0.45, Q=120.0, seed=seed + 3)
aco_tour, aco_len = aco.solve(initial_tour=initial_tour, stagnation_patience=90)
print(f" - Distance: {aco_len:.2f} | steps: {len(aco.history) - 1}")
print("\n" + "=" * 70)
print("FINAL TOURS")
print("=" * 70)
print(f"Hill Climbing: {hc_len:.2f}\n{_format_tour(hc_tour)}\n")
print(f"Genetic Algorithm: {ga_len:.2f}\n{_format_tour(ga_tour)}\n")
print(f"Ant Colony: {aco_len:.2f}\n{_format_tour(aco_tour)}\n")
best_name, best_len = min(
[("Hill Climbing", hc_len), ("Genetic Algorithm", ga_len), ("Ant Colony", aco_len)],
key=lambda x: x[1],
)
print(f"Best algorithm: {best_name} ({best_len:.2f})")
print("=" * 70)
print("\n[*] Creating ONE comparison animation...")
_anim = visualizer.animate_comparison(
histories={
"Hill Climbing": hc.history,
"Genetic Algorithm": ga.history,
"Ant Colony": aco.history,
},
colors={
"Hill Climbing": "#FF6B6B",
"Genetic Algorithm": "#4ECDC4",
"Ant Colony": "#7C5CFC",
},
interval=35,
initial_tour=initial_tour,
show_city_labels=show_labels,
fullscreen=fullscreen,
)
else:
print("Invalid choice!")
continue
except KeyboardInterrupt:
# If user interrupts from terminal, don't crash: return to menu.
print("\n[!] Interrupted. Returning to menu...")
continue
# After the window is closed, we return here and continue the menu loop.
print("\n[+] Animation closed. Returning to menu...")
if __name__ == "__main__":
main()