Path: blob/master/gui/genetic_algorithm_example.py
1028 views
# A simple program that implements the solution to the phrase generation problem using1# genetic algorithms as given in the search.ipynb notebook.2#3# Type on the home screen to change the target phrase4# Click on the slider to change genetic algorithm parameters5# Click 'GO' to run the algorithm with the specified variables6# Displays best individual of the current generation7# Displays a progress bar that indicates the amount of completion of the algorithm8# Displays the first few individuals of the current generation910import os.path11from tkinter import *12from tkinter import ttk1314import search1516sys.path.append(os.path.join(os.path.dirname(__file__), '..'))1718LARGE_FONT = ('Verdana', 12)19EXTRA_LARGE_FONT = ('Consolas', 36, 'bold')2021canvas_width = 80022canvas_height = 6002324black = '#000000'25white = '#ffffff'26p_blue = '#042533'27lp_blue = '#0c394c'2829# genetic algorithm variables30# feel free to play around with these31target = 'Genetic Algorithm' # the phrase to be generated32max_population = 100 # number of samples in each population33mutation_rate = 0.1 # probability of mutation34f_thres = len(target) # fitness threshold35ngen = 1200 # max number of generations to run the genetic algorithm3637generation = 0 # counter to keep track of generation number3839u_case = [chr(x) for x in range(65, 91)] # list containing all uppercase characters40l_case = [chr(x) for x in range(97, 123)] # list containing all lowercase characters41punctuations1 = [chr(x) for x in range(33, 48)] # lists containing punctuation symbols42punctuations2 = [chr(x) for x in range(58, 65)]43punctuations3 = [chr(x) for x in range(91, 97)]44numerals = [chr(x) for x in range(48, 58)] # list containing numbers4546# extend the gene pool with the required lists and append the space character47gene_pool = []48gene_pool.extend(u_case)49gene_pool.extend(l_case)50gene_pool.append(' ')515253# callbacks to update global variables from the slider values54def update_max_population(slider_value):55global max_population56max_population = slider_value575859def update_mutation_rate(slider_value):60global mutation_rate61mutation_rate = slider_value626364def update_f_thres(slider_value):65global f_thres66f_thres = slider_value676869def update_ngen(slider_value):70global ngen71ngen = slider_value727374# fitness function75def fitness_fn(_list):76fitness = 077# create string from list of characters78phrase = ''.join(_list)79# add 1 to fitness value for every matching character80for i in range(len(phrase)):81if target[i] == phrase[i]:82fitness += 183return fitness848586# function to bring a new frame on top87def raise_frame(frame, init=False, update_target=False, target_entry=None, f_thres_slider=None):88frame.tkraise()89global target90if update_target and target_entry is not None:91target = target_entry.get()92f_thres_slider.config(to=len(target))93if init:94population = search.init_population(max_population, gene_pool, len(target))95genetic_algorithm_stepwise(population)969798# defining root and child frames99root = Tk()100f1 = Frame(root)101f2 = Frame(root)102103# pack frames on top of one another104for frame in (f1, f2):105frame.grid(row=0, column=0, sticky='news')106107# Home Screen (f1) widgets108target_entry = Entry(f1, font=('Consolas 46 bold'), exportselection=0, foreground=p_blue, justify=CENTER)109target_entry.insert(0, target)110target_entry.pack(expand=YES, side=TOP, fill=X, padx=50)111target_entry.focus_force()112113max_population_slider = Scale(f1, from_=3, to=1000, orient=HORIZONTAL, label='Max population',114command=lambda value: update_max_population(int(value)))115max_population_slider.set(max_population)116max_population_slider.pack(expand=YES, side=TOP, fill=X, padx=40)117118mutation_rate_slider = Scale(f1, from_=0, to=1, orient=HORIZONTAL, label='Mutation rate', resolution=0.0001,119command=lambda value: update_mutation_rate(float(value)))120mutation_rate_slider.set(mutation_rate)121mutation_rate_slider.pack(expand=YES, side=TOP, fill=X, padx=40)122123f_thres_slider = Scale(f1, from_=0, to=len(target), orient=HORIZONTAL, label='Fitness threshold',124command=lambda value: update_f_thres(int(value)))125f_thres_slider.set(f_thres)126f_thres_slider.pack(expand=YES, side=TOP, fill=X, padx=40)127128ngen_slider = Scale(f1, from_=1, to=5000, orient=HORIZONTAL, label='Max number of generations',129command=lambda value: update_ngen(int(value)))130ngen_slider.set(ngen)131ngen_slider.pack(expand=YES, side=TOP, fill=X, padx=40)132133button = ttk.Button(f1, text='RUN',134command=lambda: raise_frame(f2, init=True, update_target=True, target_entry=target_entry,135f_thres_slider=f_thres_slider)).pack(side=BOTTOM, pady=50)136137# f2 widgets138canvas = Canvas(f2, width=canvas_width, height=canvas_height)139canvas.pack(expand=YES, fill=BOTH, padx=20, pady=15)140button = ttk.Button(f2, text='EXIT', command=lambda: raise_frame(f1)).pack(side=BOTTOM, pady=15)141142143# function to run the genetic algorithm and update text on the canvas144def genetic_algorithm_stepwise(population):145root.title('Genetic Algorithm')146for generation in range(ngen):147# generating new population after selecting, recombining and mutating the existing population148population = [149search.mutate(search.recombine(*search.select(2, population, fitness_fn)), gene_pool, mutation_rate) for i150in range(len(population))]151# genome with the highest fitness in the current generation152current_best = ''.join(max(population, key=fitness_fn))153# collecting first few examples from the current population154members = [''.join(x) for x in population][:48]155156# clear the canvas157canvas.delete('all')158# displays current best on top of the screen159canvas.create_text(canvas_width / 2, 40, fill=p_blue, font='Consolas 46 bold', text=current_best)160161# displaying a part of the population on the screen162for i in range(len(members) // 3):163canvas.create_text((canvas_width * .175), (canvas_height * .25 + (25 * i)), fill=lp_blue,164font='Consolas 16', text=members[3 * i])165canvas.create_text((canvas_width * .500), (canvas_height * .25 + (25 * i)), fill=lp_blue,166font='Consolas 16', text=members[3 * i + 1])167canvas.create_text((canvas_width * .825), (canvas_height * .25 + (25 * i)), fill=lp_blue,168font='Consolas 16', text=members[3 * i + 2])169170# displays current generation number171canvas.create_text((canvas_width * .5), (canvas_height * 0.95), fill=p_blue, font='Consolas 18 bold',172text=f'Generation {generation}')173174# displays blue bar that indicates current maximum fitness compared to maximum possible fitness175scaling_factor = fitness_fn(current_best) / len(target)176canvas.create_rectangle(canvas_width * 0.1, 90, canvas_width * 0.9, 100, outline=p_blue)177canvas.create_rectangle(canvas_width * 0.1, 90, canvas_width * 0.1 + scaling_factor * canvas_width * 0.8, 100,178fill=lp_blue)179canvas.update()180181# checks for completion182fittest_individual = search.fitness_threshold(fitness_fn, f_thres, population)183if fittest_individual:184break185186187raise_frame(f1)188root.mainloop()189190191