Welcome, guest | Sign In | My Account | Store | Cart
from string import digits
from random import randrange, randint
try:
    import psyco
    psyco.full(memory=100)
    psyco.profile(0.05, memory=100)
    psyco.profile(0.2)
except:
    pass
operators = "+-*/"

genes = {
    0:'0',
    1:'1',
    2:'2',
    3:'3',
    4:'4',
    5:'5',
    6:'6',
    7:'7',
    8:'8',
    9:'9',
    10:'+',
    11:'-',
    12:'*',
    13:'/'
}

traits = {}
for key, value in enumerate(genes):
    traits[value] = key

def encode(expression):
    """Takes a mathmatic expression as a string and returns a chromosome."""
    output = []
    for char in expression:
        if char in traits.keys():
            output.append(traits[char])
    return output

def decode(chromosome):
    """Takes a chromosome (list) and returns an expression."""
    # this needs to be a mini state machine.
    # We expect a stream of number + operator pairs terminated with a number
    output = ""
    need_op = False
    for key in chromosome:
        gene = genes[key]
        if need_op:
            if gene in operators:
                output += gene
                need_op = False
            else:
                continue
        else:
            if gene in digits:
                output += gene
                need_op = True
            else:
                continue
    if not need_op:
        # we don't want an op hanging off the end
        output = output[:len(output)-1]
    return output

def makeChrom(length):
    """Create a random chromosome length long."""
    output = []
    for i in range(length):
        output.append(randrange(14))
    return output

def sortFitness(chromosomes, target):
    """Sort the chromosomes so the fitest comes first."""
    done = False
    fittest = []
    for chrom in chromosomes:
        # Decode
        pheno = decode(chrom)

        # Try to evaluate
        try:
            result = eval(pheno)
        except ZeroDivisionError:
            result = 0

        # Score based on result
        fit = abs(target-result)
        if fit == 0:
            done = True
            fit = -1
        print "%s =%s : %s" %(pheno.rjust(15), str(result).rjust(10),
                                      str(fit).rjust(15))
        fittest.append((fit, chrom))
    # Once we have a list of (fitness, chrom) pairs we can sort
    fittest.sort()
    return done, [item[-1] for item in fittest]
    

def breed(pop, number, mutation):
    """Breeds a population until there are 'number' of them."""
    output = pop[:] # carry over all the breeders
    while len(output) < number:
        # Create a new chromosome by crossing two random members
        child1, child2 = crossover(pop[randint(0, len(pop)-1)],
                                   pop[randint(0, len(pop)-1)],)
        child1 = mutate(child1, mutation)
        child2 = mutate(child2, mutation)
        output.extend([child1, child2])
    if len(output) > number:
        output[:number]
    return output

def crossover(chrom1, chrom2):
    """Cross two chromosomes."""
    breakpoint = randint(0, len(chrom1))
    return (chrom1[breakpoint:] + chrom2[:breakpoint],
            chrom2[breakpoint:] + chrom1[:breakpoint])

def mutate(chrom, rate=100):
    """Randomly mutate a chromosome.
    
    Rate is the chance in 100 that mutation will occure."""
    for i in range(len(chrom)):
        chance = randint(0, 100)
        if chance <= rate:
            chrom[i] = randint(0, 13)
    return chrom

def findFit(target=42, length=20, pop=20, mutation=1):
    """Evolve a population of math problems to solve for target."""
    population = [makeChrom(length) for i in range(pop)]
    generations = 0
    while True:
        print "Generation: %s" %generations
        print "%s%s%s" %("Chromosome".rjust(15), "Result".rjust(15),
                                      "Fitness".rjust(15))
        done, population = sortFitness(population, target)
        if not done:
            # take the first several fittest members
            fittest = population[:pop/4]
            # and breed them until you have a new population
            population = breed(fittest, pop, mutation)
        else:
            print "Solution found!"
            print "%s = %s" %(decode(population[0]), eval(decode(population[0]))),
            print "is the fitest solution for %s!" %target
            return generations, decode(population[0])
        generations += 1

do_average = False
if __name__ == "__main__":
    number = input("Enter a target number for the calculation: ")
    if do_average:
        results = []
        for i in range(50):
            generations, winner = findFit(target=number, pop=30, mutation=20)
            results.append((generations, winner))
        #results.sort()
        average = 0
        for item in results:
            average += item[0]
            print item[0], item[1]
        print "Average generations: %s" %average/50
    else:
        generations, winner = findFit(target=number, pop=30, mutation=20)
        print "Generations: %s, Winner: %s" %(generations, winner)
    raw_input("Press enter to continue...")

History