Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0a0d646

Browse files
ad71norvig
authored andcommitted
Explanation of genetic algorithm functions with an example. Fixed aimacode#696 (aimacode#702)
* Added explanation of Genetic Algorithm functions using an example * Added GUI version of genetic algorithm example (phrase generation problem)
1 parent 7c5bcdd commit 0a0d646

File tree

2 files changed

+1144
-431
lines changed

2 files changed

+1144
-431
lines changed

gui/genetic_algorithm_example.py

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

0 commit comments

Comments
 (0)