From 7059a95f03548bd9793e5e07d9cb702913cbea17 Mon Sep 17 00:00:00 2001 From: alakmazaheri Date: Sat, 28 Oct 2017 17:20:32 -0400 Subject: [PATCH 1/2] Complete evolutionary algorithm toolbox --- evolve_text.py | 65 ++++++++++++++++++++++++++++++++++++-------------- testcross | 14 +++++++++++ testcross.py | 13 ++++++++++ testdist.py | 23 ++++++++++++++++++ 4 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 testcross create mode 100644 testcross.py create mode 100644 testdist.py diff --git a/evolve_text.py b/evolve_text.py index cb29dc8..87dd38a 100644 --- a/evolve_text.py +++ b/evolve_text.py @@ -18,6 +18,7 @@ from deap import algorithms from deap import base from deap import tools +from functools import lru_cache # ----------------------------------------------------------------------------- @@ -31,7 +32,6 @@ # Control whether all Messages are printed as they are evaluated VERBOSE = True - # ----------------------------------------------------------------------------- # Message object to use in evolutionary algorithm # ----------------------------------------------------------------------------- @@ -43,7 +43,6 @@ class FitnessMinimizeSingle(base.Fitness): """ weights = (-1.0, ) - class Message(list): """ Representation of an individual Message within the population to be evolved @@ -87,13 +86,27 @@ def get_text(self): """Return Message as string (rather than actual list of characters)""" return "".join(self) - # ----------------------------------------------------------------------------- # Genetic operators # ----------------------------------------------------------------------------- +@lru_cache(maxsize=None) +def levenshtein_distance(a, b): + lenA = len(a) + lenB = len(b) + + if lenA == 0: + return lenB + if lenB == 0: + return lenA -# 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 + if a[0] == b[0]: + return levenshtein_distance(a[1:], b[1:]) + l1 = levenshtein_distance(a, b[1:]) + l2 = levenshtein_distance(a[1:], b) + l3 = levenshtein_distance(a[1:], b[1:]) + dist = 1 + min(l1, l2, l3) + + return dist def evaluate_text(message, goal_text, verbose=VERBOSE): """ @@ -106,7 +119,6 @@ def evaluate_text(message, goal_text, verbose=VERBOSE): print("{msg!s}\t[Distance: {dst!s}]".format(msg=message, dst=distance)) return (distance, ) # Length 1 tuple, required by DEAP - def mutate_text(message, prob_ins=0.05, prob_del=0.05, prob_sub=0.05): """ Given a Message and independent probabilities for each mutation type, @@ -119,22 +131,40 @@ def mutate_text(message, prob_ins=0.05, prob_del=0.05, prob_sub=0.05): Substitution: Replace one character of the Message with a random (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 + randplace = int(random.random() * len(message)) + randloc = int(random.random() * len(VALID_CHARS)) + randchar = VALID_CHARS[randloc] + print(randchar) + + r = random.random() + if r < prob_ins: # Insertion + message.insert(randplace, randchar) + elif prob_ins < r < (prob_ins + prob_del): # Deletion + del message[randplace] + elif (prob_ins + prob_del) < r < (prob_ins + prob_del + prob_sub): # Substitution + message[randplace] = randchar + else: + pass # Most of the time, do not mutate return (message, ) # Length 1 tuple, required by DEAP - # ----------------------------------------------------------------------------- # DEAP Toolbox and Algorithm setup # ----------------------------------------------------------------------------- +def two_point_crossover(a, b): + """ + Return tuple of crossed parent strings a, b + >>> two_point_crossover("ABCDEF", "UVWXYZ") + ("ABWXYF", "UVCDEZ") + """ + maxcrosslen = min(len(a), len(b)) + cx1 = random.randint(1, maxcrosslen) + cx2 = random.randint(1, maxcrosslen - 1) + + anew = a[:cx1] + b[cx1:cx2] + a[cx2:] + bnew = b[:cx1] + a[cx1:cx2] + b[cx2:] + + return (Message("".join(anew)), Message("".join(bnew))) def get_toolbox(text): """Return DEAP Toolbox configured to evolve given 'text' string""" @@ -149,7 +179,7 @@ def get_toolbox(text): # Genetic operators toolbox.register("evaluate", evaluate_text, goal_text=text) - toolbox.register("mate", tools.cxTwoPoint) + toolbox.register("mate", two_point_crossover) toolbox.register("mutate", mutate_text) toolbox.register("select", tools.selTournament, tournsize=3) @@ -190,7 +220,6 @@ def evolve_string(text): return pop, log - # ----------------------------------------------------------------------------- # Run if called from the command line # ----------------------------------------------------------------------------- diff --git a/testcross b/testcross new file mode 100644 index 0000000..b9536fc --- /dev/null +++ b/testcross @@ -0,0 +1,14 @@ +def two_point_crossover(a, b): + """ + Return tuple of crossed parent strings a, b + >>> two_point_crossover("ABCDEF", "UVWXYZ") + ("ABWXYF", "UVCDEZ") + """ + maxcrosslen = min(len(a), len(b)) + cx1 = random.randint(1, maxcrosslen) + cx2 = random.randint(1, maxcrosslen - 1) + + anew = a[:cx1] + b[cx1:cx2] + a[cx2:] + bnew = b[:cx1] + a[cx1:cx2] + b[cx2:] + + return (anew, bnew) diff --git a/testcross.py b/testcross.py new file mode 100644 index 0000000..2344fc5 --- /dev/null +++ b/testcross.py @@ -0,0 +1,13 @@ +import random + +def two_point_crossover(a, b): + maxcrosslen = min(len(a), len(b)) + cx1 = random.randint(1, maxcrosslen) + cx2 = random.randint(1, maxcrosslen - 1) + + anew = a[:cx1] + b[cx1:cx2] + a[cx2:] + bnew = b[:cx1] + a[cx1:cx2] + b[cx2:] + + return (anew, bnew) + +print(two_point_crossover("ABCDEF", "UVWXYZ")) diff --git a/testdist.py b/testdist.py new file mode 100644 index 0000000..53ef8b1 --- /dev/null +++ b/testdist.py @@ -0,0 +1,23 @@ +from functools import lru_cache + +@lru_cache(maxsize=None) + +def levenshtein_distance(a, b): + lenA = len(a) + lenB = len(b) + + if lenA == 0: + return lenB + if lenB == 0: + return lenA + + if a[0] == b[0]: + return levenshtein_distance(a[1:], b[1:]) + l1 = levenshtein_distance(a, b[1:]) + l2 = levenshtein_distance(a[1:], b) + l3 = levenshtein_distance(a[1:], b[1:]) + dist = 1 + min(l1, l2, l3) + + return dist + +print(levenshtein_distance('kitten', 'sitting')) From 59330fd92bd4d3f9b6c03149abdb4bdf473c926e Mon Sep 17 00:00:00 2001 From: alakmazaheri Date: Sat, 28 Oct 2017 17:21:03 -0400 Subject: [PATCH 2/2] Complete evolutionary algorithm toolbox --- testcross | 14 -------------- testcross.py | 13 ------------- testdist.py | 23 ----------------------- 3 files changed, 50 deletions(-) delete mode 100644 testcross delete mode 100644 testcross.py delete mode 100644 testdist.py diff --git a/testcross b/testcross deleted file mode 100644 index b9536fc..0000000 --- a/testcross +++ /dev/null @@ -1,14 +0,0 @@ -def two_point_crossover(a, b): - """ - Return tuple of crossed parent strings a, b - >>> two_point_crossover("ABCDEF", "UVWXYZ") - ("ABWXYF", "UVCDEZ") - """ - maxcrosslen = min(len(a), len(b)) - cx1 = random.randint(1, maxcrosslen) - cx2 = random.randint(1, maxcrosslen - 1) - - anew = a[:cx1] + b[cx1:cx2] + a[cx2:] - bnew = b[:cx1] + a[cx1:cx2] + b[cx2:] - - return (anew, bnew) diff --git a/testcross.py b/testcross.py deleted file mode 100644 index 2344fc5..0000000 --- a/testcross.py +++ /dev/null @@ -1,13 +0,0 @@ -import random - -def two_point_crossover(a, b): - maxcrosslen = min(len(a), len(b)) - cx1 = random.randint(1, maxcrosslen) - cx2 = random.randint(1, maxcrosslen - 1) - - anew = a[:cx1] + b[cx1:cx2] + a[cx2:] - bnew = b[:cx1] + a[cx1:cx2] + b[cx2:] - - return (anew, bnew) - -print(two_point_crossover("ABCDEF", "UVWXYZ")) diff --git a/testdist.py b/testdist.py deleted file mode 100644 index 53ef8b1..0000000 --- a/testdist.py +++ /dev/null @@ -1,23 +0,0 @@ -from functools import lru_cache - -@lru_cache(maxsize=None) - -def levenshtein_distance(a, b): - lenA = len(a) - lenB = len(b) - - if lenA == 0: - return lenB - if lenB == 0: - return lenA - - if a[0] == b[0]: - return levenshtein_distance(a[1:], b[1:]) - l1 = levenshtein_distance(a, b[1:]) - l2 = levenshtein_distance(a[1:], b) - l3 = levenshtein_distance(a[1:], b[1:]) - dist = 1 + min(l1, l2, l3) - - return dist - -print(levenshtein_distance('kitten', 'sitting'))