Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aimacode
GitHub Repository: aimacode/aima-python
Path: blob/master/gui/genetic_algorithm_example.py
621 views
1
# A simple program that implements the solution to the phrase generation problem using
2
# genetic algorithms as given in the search.ipynb notebook.
3
#
4
# Type on the home screen to change the target phrase
5
# Click on the slider to change genetic algorithm parameters
6
# Click 'GO' to run the algorithm with the specified variables
7
# Displays best individual of the current generation
8
# Displays a progress bar that indicates the amount of completion of the algorithm
9
# Displays the first few individuals of the current generation
10
11
import os.path
12
from tkinter import *
13
from tkinter import ttk
14
15
import search
16
17
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
18
19
LARGE_FONT = ('Verdana', 12)
20
EXTRA_LARGE_FONT = ('Consolas', 36, 'bold')
21
22
canvas_width = 800
23
canvas_height = 600
24
25
black = '#000000'
26
white = '#ffffff'
27
p_blue = '#042533'
28
lp_blue = '#0c394c'
29
30
# genetic algorithm variables
31
# feel free to play around with these
32
target = 'Genetic Algorithm' # the phrase to be generated
33
max_population = 100 # number of samples in each population
34
mutation_rate = 0.1 # probability of mutation
35
f_thres = len(target) # fitness threshold
36
ngen = 1200 # max number of generations to run the genetic algorithm
37
38
generation = 0 # counter to keep track of generation number
39
40
u_case = [chr(x) for x in range(65, 91)] # list containing all uppercase characters
41
l_case = [chr(x) for x in range(97, 123)] # list containing all lowercase characters
42
punctuations1 = [chr(x) for x in range(33, 48)] # lists containing punctuation symbols
43
punctuations2 = [chr(x) for x in range(58, 65)]
44
punctuations3 = [chr(x) for x in range(91, 97)]
45
numerals = [chr(x) for x in range(48, 58)] # list containing numbers
46
47
# extend the gene pool with the required lists and append the space character
48
gene_pool = []
49
gene_pool.extend(u_case)
50
gene_pool.extend(l_case)
51
gene_pool.append(' ')
52
53
54
# callbacks to update global variables from the slider values
55
def update_max_population(slider_value):
56
global max_population
57
max_population = slider_value
58
59
60
def update_mutation_rate(slider_value):
61
global mutation_rate
62
mutation_rate = slider_value
63
64
65
def update_f_thres(slider_value):
66
global f_thres
67
f_thres = slider_value
68
69
70
def update_ngen(slider_value):
71
global ngen
72
ngen = slider_value
73
74
75
# fitness function
76
def fitness_fn(_list):
77
fitness = 0
78
# create string from list of characters
79
phrase = ''.join(_list)
80
# add 1 to fitness value for every matching character
81
for i in range(len(phrase)):
82
if target[i] == phrase[i]:
83
fitness += 1
84
return fitness
85
86
87
# function to bring a new frame on top
88
def raise_frame(frame, init=False, update_target=False, target_entry=None, f_thres_slider=None):
89
frame.tkraise()
90
global target
91
if update_target and target_entry is not None:
92
target = target_entry.get()
93
f_thres_slider.config(to=len(target))
94
if init:
95
population = search.init_population(max_population, gene_pool, len(target))
96
genetic_algorithm_stepwise(population)
97
98
99
# defining root and child frames
100
root = Tk()
101
f1 = Frame(root)
102
f2 = Frame(root)
103
104
# pack frames on top of one another
105
for frame in (f1, f2):
106
frame.grid(row=0, column=0, sticky='news')
107
108
# Home Screen (f1) widgets
109
target_entry = Entry(f1, font=('Consolas 46 bold'), exportselection=0, foreground=p_blue, justify=CENTER)
110
target_entry.insert(0, target)
111
target_entry.pack(expand=YES, side=TOP, fill=X, padx=50)
112
target_entry.focus_force()
113
114
max_population_slider = Scale(f1, from_=3, to=1000, orient=HORIZONTAL, label='Max population',
115
command=lambda value: update_max_population(int(value)))
116
max_population_slider.set(max_population)
117
max_population_slider.pack(expand=YES, side=TOP, fill=X, padx=40)
118
119
mutation_rate_slider = Scale(f1, from_=0, to=1, orient=HORIZONTAL, label='Mutation rate', resolution=0.0001,
120
command=lambda value: update_mutation_rate(float(value)))
121
mutation_rate_slider.set(mutation_rate)
122
mutation_rate_slider.pack(expand=YES, side=TOP, fill=X, padx=40)
123
124
f_thres_slider = Scale(f1, from_=0, to=len(target), orient=HORIZONTAL, label='Fitness threshold',
125
command=lambda value: update_f_thres(int(value)))
126
f_thres_slider.set(f_thres)
127
f_thres_slider.pack(expand=YES, side=TOP, fill=X, padx=40)
128
129
ngen_slider = Scale(f1, from_=1, to=5000, orient=HORIZONTAL, label='Max number of generations',
130
command=lambda value: update_ngen(int(value)))
131
ngen_slider.set(ngen)
132
ngen_slider.pack(expand=YES, side=TOP, fill=X, padx=40)
133
134
button = ttk.Button(f1, text='RUN',
135
command=lambda: raise_frame(f2, init=True, update_target=True, target_entry=target_entry,
136
f_thres_slider=f_thres_slider)).pack(side=BOTTOM, pady=50)
137
138
# f2 widgets
139
canvas = Canvas(f2, width=canvas_width, height=canvas_height)
140
canvas.pack(expand=YES, fill=BOTH, padx=20, pady=15)
141
button = ttk.Button(f2, text='EXIT', command=lambda: raise_frame(f1)).pack(side=BOTTOM, pady=15)
142
143
144
# function to run the genetic algorithm and update text on the canvas
145
def genetic_algorithm_stepwise(population):
146
root.title('Genetic Algorithm')
147
for generation in range(ngen):
148
# generating new population after selecting, recombining and mutating the existing population
149
population = [
150
search.mutate(search.recombine(*search.select(2, population, fitness_fn)), gene_pool, mutation_rate) for i
151
in range(len(population))]
152
# genome with the highest fitness in the current generation
153
current_best = ''.join(max(population, key=fitness_fn))
154
# collecting first few examples from the current population
155
members = [''.join(x) for x in population][:48]
156
157
# clear the canvas
158
canvas.delete('all')
159
# displays current best on top of the screen
160
canvas.create_text(canvas_width / 2, 40, fill=p_blue, font='Consolas 46 bold', text=current_best)
161
162
# displaying a part of the population on the screen
163
for i in range(len(members) // 3):
164
canvas.create_text((canvas_width * .175), (canvas_height * .25 + (25 * i)), fill=lp_blue,
165
font='Consolas 16', text=members[3 * i])
166
canvas.create_text((canvas_width * .500), (canvas_height * .25 + (25 * i)), fill=lp_blue,
167
font='Consolas 16', text=members[3 * i + 1])
168
canvas.create_text((canvas_width * .825), (canvas_height * .25 + (25 * i)), fill=lp_blue,
169
font='Consolas 16', text=members[3 * i + 2])
170
171
# displays current generation number
172
canvas.create_text((canvas_width * .5), (canvas_height * 0.95), fill=p_blue, font='Consolas 18 bold',
173
text=f'Generation {generation}')
174
175
# displays blue bar that indicates current maximum fitness compared to maximum possible fitness
176
scaling_factor = fitness_fn(current_best) / len(target)
177
canvas.create_rectangle(canvas_width * 0.1, 90, canvas_width * 0.9, 100, outline=p_blue)
178
canvas.create_rectangle(canvas_width * 0.1, 90, canvas_width * 0.1 + scaling_factor * canvas_width * 0.8, 100,
179
fill=lp_blue)
180
canvas.update()
181
182
# checks for completion
183
fittest_individual = search.fitness_threshold(fitness_fn, f_thres, population)
184
if fittest_individual:
185
break
186
187
188
raise_frame(f1)
189
root.mainloop()
190
191