Feb 2 2009

Hello Gene

While on breaks at work I’ve been reading a bit about Genetic Programming. The following is a quick-and-dirty python script I threw together (mostly for myself) the other night. It evolves the target string “Hello World” over several generations, starting with random strings. After a bit of experimentation I found that a population size of 300 seems to work best for this.

import random
import string
 
class GenePool:
    def __init__(self, population_size):
        self.population_size = population_size
        self.population = [self.generate()
                           for i in range(population_size)]
 
    def run(self):
        i = 0
        while True:
            print "Generation " + str(i) + ": " + self.population[0]
            i = i + 1
            if self.step():
                return
 
 
    def step(self):
        fittest = self.get_best(self.population_size/2)
        if self.successful(fittest[0]):
            print "Success! " + fittest[0]
            return True
        else:
            self.population = self.get_new_generation(fittest)
            return False
 
    def generate(self):
        return "".join([random.choice(string.ascii_letters + " ")
                         for i in range(11)])
 
    def evaluate(self, individual):
        s = "Hello World"
        total = 0
        for idx in range(len(s)):
            if individual[idx] == s[idx]:
                total = total + 1
        return total / 11.0
 
    def get_best(self, n):
        fitness = []
        for idx, individual in enumerate(self.population):
            fitness.append((idx, self.evaluate(individual)))
        fitness.sort(cmp=lambda x,y: cmp(x[1], y[1]), reverse=True)
        best = []
        return [self.population[tup[0]] for tup in fitness[:n]]
 
    def successful(self, individual):
        return individual == "Hello World"
 
    def get_new_generation(self, individuals):
        population = []
        while len(population) < int(self.population_size * 0.75):
            population.extend(self.breed(
                                    random.choice(individuals),
                                    random.choice(individuals)))
        while len(population) < self.population_size:
            population.append(self.generate())
        return population
 
    def breed(self, mother, father):
        point1 = random.randint(0,len(mother)-2)
        point2 = random.randint(point1+1, len(mother)-1)
        new1 = mother[:point1] + father[point1:point2] + mother[point2:]
        new2 = father[:point1] + mother[point1:point2] + father[point2:]
        if random.random() < 0.05:
            new1 = self.mutate(new1)
        if random.random() < 0.05:
            new2 = self.mutate(new2)
        return (new1, new2)
 
    def mutate(self, individual):
        x = list(individual)
        x[random.randint(0, len(x)-1)] = random.choice(
                                            random.choice(
                                              string.asciiletters + " "))
        return "".join(x)
 
if __name__ == "__main__":
    G = GenePool(300)
    G.run()