# Examples of genetic algorithms from CS 151 class Tuesday, 11/22/05

from pyrobot.brain.ga import *
import math

#-----------------------------------------------------------------------------
# GA for evolving a binary string of all 1's

class OnesGA(GA):

    def fitnessFunction(self, i):
        # fitness of the ith individual = number of 1's
        genome = self.pop.individuals[i].genotype
        count = 0
        for bit in genome:
            count += bit
        return count

    def isDone(self):
        # stop GA when highest-fit individual is found
        return self.pop.bestMember.genotype == \
               [1] * len(self.pop.bestMember.genotype)


ga1 = OnesGA(Population(100, Gene, size=64, mode='bit', verbose=True,
                        elitePercent=0.1),
             mutationRate=0.005, crossoverRate=0.75, verbose=True,
             maxGeneration=300)

#-----------------------------------------------------------------------------
# GA described in the article "Survival of the Fittest Bits", which was
# handed out in class.  To reproduce the graph of the function, do this
# in gnuplot:
#
# gnuplot> set samples 800
# gnuplot> plot [0:3.14159][0:4.25] x+abs(sin(32*x))

def binaryToInteger(b):
    "Converts a list b of bits to its equivalent integer value"
    value = 0
    for i in range(len(b)):
        value += b[i] * 2 ** (len(b) - i - 1)
    return value

def integerToBinary(n, L):
    "Converts an integer n to its equivalent L-bit binary representation"
    if n < 2:
        return [0] * (L - 1) + [n]
    else:
        return integerToBinary(n / 2, L - 1) + [n % 2]

def findbest(L):
    "Prints out the L-bit binary encoding of the x value with highest fitness"
    bestGenome, bestX, bestFitness = None, None, -1
    for i in range(2 ** L):
        genome = integerToBinary(i, L)
        x = genomeToX(genome)
        fitness = f(x)
        if fitness > bestFitness:
            bestGenome, bestX, bestFitness = genome, x, fitness
    print 'best %d-bit genome for current function f:' % L
    print '%s --> x = %.5f, f(x) = %.10f' % (bestGenome, bestX, bestFitness)

def genomeToX(b):
    "Converts a genome to its equivalent x value in the range 0 <= x < pi"
    return binaryToInteger(b) * math.pi / (2 ** len(b))
            
def f(x):
    return x + abs(math.sin(32 * x))

class SineGA(GA):

    def fitnessFunction(self, i):
        # fitness of the ith individual = value of f(x)
        return f(genomeToX(self.pop.individuals[i].genotype))

    def isDone(self):
        # stop GA when highest-fit individual is found
        return self.pop.bestMember.genotype == \
               [1,1,1,1,1,1,0,0,0,0,0,1,0,1,0,0]


ga2 = SineGA(Population(50, Gene, size=16, mode='bit', verbose=True,
                        elitePercent=0.02),
             mutationRate=0.005, crossoverRate=0.75, verbose=True,
             maxGeneration=300)

#------------------------------------------------------------------------------
# GA for evolving a phrase

targetPhrase = "the rain in spain stays mainly in the plain"

class PhraseGA(GA):

    def fitnessFunction(self, i):
        # fitness of the ith individual = number of letters correct
        genome = self.pop.individuals[i].genotype
        correct = 0
        for i in range(len(targetPhrase)):
            correct += genome[i] == targetPhrase[i]
        return correct

    def isDone(self):
        # stop GA when highest-fit individual is found
        return self.pop.bestMember.genotype == targetPhrase


ga3 = PhraseGA(Population(100, Gene, size=len(targetPhrase), mode='char',
                          verbose=True, elitePercent=0.1),
               mutationRate=0.005, crossoverRate=0.75, verbose=True,
               maxGeneration=300)

#------------------------------------------------------------------------------

# choose a GA to run
ga = ga1
#ga = ga2
#ga = ga3

# Save the average and best fitness values for each generation to the files
# "GAAvgFitness" and "GABestFitness"
ga.logAverageFitness()
ga.logBestFitness()

# start the run
ga.evolve()

# You can use gnuplot to see the resulting curves for average and best fitness
# over time (make sure to exit python first to force the log files to close).
# For example, for a run of 300 generations with a maximum possible fitness
# value of 50, do this:
# 
# gnuplot> set style data lines
# gnuplot> plot [0:300][0:50] "GAAvgFitness", "GABestFitness"

