Skip to content

Evolutionary Algorithms Done 8/5 Complete #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 58 additions & 12 deletions evolve_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
https://sites.google.com/site/sd15spring/home/project-toolbox/evolutionary-algorithms
"""


"""
Completed by Kevin Zhang

Software Design Spring 2016

"""

import random
import string

Expand Down Expand Up @@ -92,8 +100,32 @@ def get_text(self):
# Genetic operators
#-----------------------------------------------------------------------------

# TODO: Implement levenshtein_distance function (see Day 9 in-class exercises)
# HINT: Now would be a great time to implement memoization if you haven't

dicts = {}

def levenshtein_distance(a, b):
"""
The levenshtein distance between two strings, using memoization
"""

if (a,b) in dicts:
return dicts[a,b]
else:
if len(a)==0:
return len(b)
elif len(b)==0:
return len(a)
elif a[0] == b[0]:
option1 = levenshtein_distance(a[1:],b[1:])
else:
option1 = 1 + levenshtein_distance(a[1:],b[1:])
option2 = 1 + levenshtein_distance(a[1:],b)
option3 = 1 + levenshtein_distance(a,b[1:])

minimum = min(option1,option2,option3)

dicts[a,b] = minimum
return minimum

def evaluate_text(message, goal_text, verbose=VERBOSE):
"""
Expand All @@ -120,18 +152,32 @@ def mutate_text(message, prob_ins=0.05, prob_del=0.05, prob_sub=0.05):
(legal) character
"""

if random.random() < prob_ins:
# TODO: Implement insertion-type mutation
pass

# TODO: Also implement deletion and substitution mutations
# HINT: Message objects inherit from list, so they also inherit
# useful list methods
# HINT: You probably want to use the VALID_CHARS global variable
if random.random() < prob_ins: #inserting a character
spot = random.randint(0, len(message)-1)
message.insert(spot, random.choice(VALID_CHARS))
if random.random() < prob_del: #deleting a character
spot = random.randint(0, len(message)-1)
message.remove(message[spot])
if random.random() < prob_sub: #subsituting a character
spot = random.randint(0, len(message)-1)
message[spot] = random.choice(VALID_CHARS)

return (message, ) # Length 1 tuple, required by DEAP


def crossover(parent1, parent2):
"""
Uses a genetic crossover to mate two parents and create two offspring
Parent 1 and 2 are Message objects, and the returned tuple will also be Messages
"""
cross1 = random.randint(0, min(len(parent1), len(parent2))-2) #creating the first crossing point
cross2 = random.randint(cross1+1, min(len(parent1), len(parent2))-1) #creating the second crossing point

offspring1 = Message(parent1[0:cross1] + parent2[cross1:cross2] + parent1[cross2:])
offspring2 = Message(parent2[0:cross1] + parent1[cross1:cross2] + parent2[cross2:])

return offspring1, offspring2 #length 2 tuple, which matches thet output of the original method used

#-----------------------------------------------------------------------------
# DEAP Toolbox and Algorithm setup
#-----------------------------------------------------------------------------
Expand All @@ -149,7 +195,7 @@ def get_toolbox(text):

# Genetic operators
toolbox.register("evaluate", evaluate_text, goal_text=text)
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mate", crossover)
toolbox.register("mutate", mutate_text)
toolbox.register("select", tools.selTournament, tournsize=3)

Expand All @@ -170,7 +216,7 @@ def evolve_string(text):

# Get configured toolbox and create a population of random Messages
toolbox = get_toolbox(text)
pop = toolbox.population(n=300)
pop = toolbox.population(n=1000)

# Collect statistics as the EA runs
stats = tools.Statistics(lambda ind: ind.fitness.values)
Expand Down
17 changes: 17 additions & 0 deletions results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Evolutionary Toolbox

Changing population size

Population size has a big effect on the results in terms of its progression. Having a larger population size means having a larger starting pool and thus more chances of getting the target from the beginning. More Messages means more possible mutations and crossovers, thus creating a better chance for obtaining the goal. A smaller populations means that within the given number of generations there are less Messages to attempt to get the goal. This was confirmed with an experiment of using a starting population of 1000 and of 10. The 1000 population got the goal by the 150th generation, where the 10 population was still a distance of 13 away from the goal by the 500 generation. Population is very important when trying to evolve, as evidently larger populations evolve faster.

Changing generation size

Generation size is quite similar to population in that it has a big effect on the progress of the goal. A larger generation window means more time to evolve towards the goal, and a smaller one means less time. A population with a longer generation window will most likely evolve and develop farther than a similar population with a shorter time window. This was confirmed with a population of 100 given a generation of 10 and 1000. The 10 generation population only made it to a distance of 12 away from the goal, whereas the 1000 generation population managed to get down to 2. The inability to reach the goal also shows that population still plays a factor in the generation perspective and that the two do not fulfill the same niche in affecting evolution.

Changing the crossing over probability

It appears that changing the crossing over probability does not severely affect the evolution process. This might seem counter-intuitive, as mating would allow for better chances to find the goal. In all honesty, I would agree with that statement. But apparently my algorithm says otherwise. When testing a probability of both .1 and .9, the results were about the same. A 0.1 probability for crossing over got the population to the goal within a distance of 2, and a 0.9 probability for crossing over got the population to the goal within a distance of 3. Perhaps given the size of the population which was 300 or the number of generations was which 500, as long as the probability wasn't 0 or 1.0 it might be about the same given the size.

Changing the mutation probability

Changing the mutation probability seems to require a fine balance. Mutations are needed for the population to evolve and find individuals that are closer to the goal, but too many might be damaging to the evolution process and prohibit the ability of the population to make it to the goal in a smooth fashion. This was more or less proven with a mutation probability of .01 and of .9. Both populations ended up about a distance of 3-5 from the goal. However, a probability of .05 or .3 got the population to a distance of 1. It is apparent that mutation must then be in the middle ground, with not too much but not too little.