1
1
from tkinter import *
2
+ from tkinter import messagebox
2
3
import sys
3
4
import os .path
4
5
sys .path .append (os .path .join (os .path .dirname (__file__ ), '..' ))
5
6
from search import *
7
+ import utils
6
8
import numpy as np
7
9
8
10
distances = {}
@@ -56,6 +58,7 @@ def __init__(self, root, all_cities):
56
58
self .calculate_canvas_size ()
57
59
self .button_text = StringVar ()
58
60
self .button_text .set ("Start" )
61
+ self .algo_var = StringVar ()
59
62
self .all_cities = all_cities
60
63
self .frame_select_cities = Frame (self .root )
61
64
self .frame_select_cities .grid (row = 1 )
@@ -85,9 +88,18 @@ def create_buttons(self):
85
88
""" Create start and quit button """
86
89
87
90
Button (self .frame_select_cities , textvariable = self .button_text ,
88
- command = self .run_traveling_salesman ).grid (row = 3 , column = 4 , sticky = E + W )
89
- Button (self .frame_select_cities , text = 'Quit' , command = self .root .destroy ).grid (
90
- row = 3 , column = 5 , sticky = E + W )
91
+ command = self .run_traveling_salesman ).grid (row = 5 , column = 4 , sticky = E + W )
92
+ Button (self .frame_select_cities , text = 'Quit' , command = self .on_closing ).grid (
93
+ row = 5 , column = 5 , sticky = E + W )
94
+
95
+ def create_dropdown_menu (self ):
96
+ """ Create dropdown menu for algorithm selection """
97
+
98
+ choices = {'Simulated Annealing' , 'Genetic Algorithm' , 'Hill Climbing' }
99
+ self .algo_var .set ('Simulated Annealing' )
100
+ dropdown_menu = OptionMenu (self .frame_select_cities , self .algo_var , * choices )
101
+ dropdown_menu .grid (row = 4 , column = 4 , columnspan = 2 , sticky = E + W )
102
+ dropdown_menu .config (width = 19 )
91
103
92
104
def run_traveling_salesman (self ):
93
105
""" Choose selected citites """
@@ -151,13 +163,30 @@ def create_canvas(self, problem):
151
163
variable = self .speed , label = "Speed ----> " , showvalue = 0 , font = "Times 11" ,
152
164
relief = "sunken" , cursor = "gumby" )
153
165
speed_scale .grid (row = 1 , columnspan = 5 , sticky = N + S + E + W )
154
- self .temperature = IntVar ()
155
- temperature_scale = Scale (self .frame_canvas , from_ = 100 , to = 0 , orient = HORIZONTAL ,
166
+
167
+ if self .algo_var .get () == 'Simulated Annealing' :
168
+ self .temperature = IntVar ()
169
+ temperature_scale = Scale (self .frame_canvas , from_ = 100 , to = 0 , orient = HORIZONTAL ,
156
170
length = 200 , variable = self .temperature , label = "Temperature ---->" ,
157
171
font = "Times 11" , relief = "sunken" , showvalue = 0 , cursor = "gumby" )
158
-
159
- temperature_scale .grid (row = 1 , column = 5 , columnspan = 5 , sticky = N + S + E + W )
160
- self .simulated_annealing_with_tunable_T (problem , map_canvas )
172
+ temperature_scale .grid (row = 1 , column = 5 , columnspan = 5 , sticky = N + S + E + W )
173
+ self .simulated_annealing_with_tunable_T (problem , map_canvas )
174
+ elif self .algo_var .get () == 'Genetic Algorithm' :
175
+ self .mutation_rate = DoubleVar ()
176
+ self .mutation_rate .set (0.05 )
177
+ mutation_rate_scale = Scale (self .frame_canvas , from_ = 0 , to = 1 , orient = HORIZONTAL ,
178
+ length = 200 , variable = self .mutation_rate , label = 'Mutation Rate ---->' ,
179
+ font = 'Times 11' , relief = 'sunken' , showvalue = 0 , cursor = 'gumby' , resolution = 0.001 )
180
+ mutation_rate_scale .grid (row = 1 , column = 5 , columnspan = 5 , sticky = 'nsew' )
181
+ self .genetic_algorithm (problem , map_canvas )
182
+ elif self .algo_var .get () == 'Hill Climbing' :
183
+ self .no_of_neighbors = IntVar ()
184
+ self .no_of_neighbors .set (100 )
185
+ no_of_neighbors_scale = Scale (self .frame_canvas , from_ = 10 , to = 1000 , orient = HORIZONTAL ,
186
+ length = 200 , variable = self .no_of_neighbors , label = 'Number of neighbors ---->' ,
187
+ font = 'Times 11' ,relief = 'sunken' , showvalue = 0 , cursor = 'gumby' )
188
+ no_of_neighbors_scale .grid (row = 1 , column = 5 , columnspan = 5 , sticky = 'nsew' )
189
+ self .hill_climbing (problem , map_canvas )
161
190
162
191
def exp_schedule (k = 100 , lam = 0.03 , limit = 1000 ):
163
192
""" One possible schedule function for simulated annealing """
@@ -191,6 +220,102 @@ def simulated_annealing_with_tunable_T(self, problem, map_canvas, schedule=exp_s
191
220
map_canvas .update ()
192
221
map_canvas .after (self .speed .get ())
193
222
223
+ def genetic_algorithm (self , problem , map_canvas ):
224
+ """ Genetic Algorithm modified for the given problem """
225
+
226
+ def init_population (pop_number , gene_pool , state_length ):
227
+ """ initialize population """
228
+
229
+ population = []
230
+ for i in range (pop_number ):
231
+ population .append (utils .shuffled (gene_pool ))
232
+ return population
233
+
234
+ def recombine (state_a , state_b ):
235
+ """ recombine two problem states """
236
+
237
+ start = random .randint (0 , len (state_a ) - 1 )
238
+ end = random .randint (start + 1 , len (state_a ))
239
+ new_state = state_a [start :end ]
240
+ for city in state_b :
241
+ if city not in new_state :
242
+ new_state .append (city )
243
+ return new_state
244
+
245
+ def mutate (state , mutation_rate ):
246
+ """ mutate problem states """
247
+
248
+ if random .uniform (0 , 1 ) < mutation_rate :
249
+ sample = random .sample (range (len (state )), 2 )
250
+ state [sample [0 ]], state [sample [1 ]] = state [sample [1 ]], state [sample [0 ]]
251
+ return state
252
+
253
+ def fitness_fn (state ):
254
+ """ calculate fitness of a particular state """
255
+
256
+ fitness = problem .value (state )
257
+ return int ((5600 + fitness ) ** 2 )
258
+
259
+ current = Node (problem .initial )
260
+ population = init_population (100 , current .state , len (current .state ))
261
+ all_time_best = current .state
262
+ while (1 ):
263
+ population = [mutate (recombine (* select (2 , population , fitness_fn )), self .mutation_rate .get ()) for i in range (len (population ))]
264
+ current_best = utils .argmax (population , key = fitness_fn )
265
+ if fitness_fn (current_best ) > fitness_fn (all_time_best ):
266
+ all_time_best = current_best
267
+ self .cost .set ("Cost = " + str ('%0.3f' % (- 1 * problem .value (all_time_best ))))
268
+ map_canvas .delete ('poly' )
269
+ points = []
270
+ for city in current_best :
271
+ points .append (self .frame_locations [city ][0 ])
272
+ points .append (self .frame_locations [city ][1 ])
273
+ map_canvas .create_polygon (points , outline = 'red' , width = 1 , fill = '' , tag = 'poly' )
274
+ best_points = []
275
+ for city in all_time_best :
276
+ best_points .append (self .frame_locations [city ][0 ])
277
+ best_points .append (self .frame_locations [city ][1 ])
278
+ map_canvas .create_polygon (best_points , outline = 'red' , width = 3 , fill = '' , tag = 'poly' )
279
+ map_canvas .update ()
280
+ map_canvas .after (self .speed .get ())
281
+
282
+ def hill_climbing (self , problem , map_canvas ):
283
+ """ hill climbing where number of neighbors is taken as user input """
284
+
285
+ def find_neighbors (state , number_of_neighbors = 100 ):
286
+ """ finds neighbors using two_opt method """
287
+
288
+ neighbors = []
289
+ for i in range (number_of_neighbors ):
290
+ new_state = problem .two_opt (state )
291
+ neighbors .append (Node (new_state ))
292
+ state = new_state
293
+ return neighbors
294
+
295
+ current = Node (problem .initial )
296
+ while (1 ):
297
+ neighbors = find_neighbors (current .state , self .no_of_neighbors .get ())
298
+ neighbor = utils .argmax_random_tie (neighbors , key = lambda node : problem .value (node .state ))
299
+ map_canvas .delete ('poly' )
300
+ points = []
301
+ for city in current .state :
302
+ points .append (self .frame_locations [city ][0 ])
303
+ points .append (self .frame_locations [city ][1 ])
304
+ map_canvas .create_polygon (points , outline = 'red' , width = 3 , fill = '' , tag = 'poly' )
305
+ neighbor_points = []
306
+ for city in neighbor .state :
307
+ neighbor_points .append (self .frame_locations [city ][0 ])
308
+ neighbor_points .append (self .frame_locations [city ][1 ])
309
+ map_canvas .create_polygon (neighbor_points , outline = 'red' , width = 1 , fill = '' , tag = 'poly' )
310
+ map_canvas .update ()
311
+ map_canvas .after (self .speed .get ())
312
+ if problem .value (neighbor .state ) > problem .value (current .state ):
313
+ current .state = neighbor .state
314
+ self .cost .set ("Cost = " + str ('%0.3f' % (- 1 * problem .value (current .state ))))
315
+
316
+ def on_closing (self ):
317
+ if messagebox .askokcancel ('Quit' , 'Do you want to quit?' ):
318
+ self .root .destroy ()
194
319
195
320
def main ():
196
321
all_cities = []
@@ -212,6 +337,8 @@ def main():
212
337
cities_selection_panel = TSP_Gui (root , all_cities )
213
338
cities_selection_panel .create_checkboxes ()
214
339
cities_selection_panel .create_buttons ()
340
+ cities_selection_panel .create_dropdown_menu ()
341
+ root .protocol ('WM_DELETE_WINDOW' , cities_selection_panel .on_closing )
215
342
root .mainloop ()
216
343
217
344
0 commit comments