Benjamin Elfner
Benjamin Elfner

Reputation: 13

Python genetic algorithm

The evolve function is having problems as is the mutate one.

    from random import randint, random
    from operator import add
    from functools import reduce



   def individual(length, min, max):
        'Create a member of the population.'
        return [randint(min,max) for x in range(length)]


    def population(count, length, min, max):
        'Create a number of individuals (i.e. a population).'
        return [ individual(length, min, max) for x in range(count) ]


    def fitness(individual, target):
        'Determine the fitness of an individual. Lower is better.'
        sum = reduce(add, individual, 0)
        return abs(target-sum)


    def grade(pop, target):
        'Find average fitness for a population.'
        summed = reduce(add, (fitness(x, target) for x in pop), 0)
        return summed / (len(pop) * 1.0)


    chance_to_mutate = 0.01
    for i in p:
        if chance_to_mutate > random():
            place_to_modify = randint(0,len(i))
            i[place_to_modify] = randint(min(i), max(i))


    def evolve(pop, target, retain=0.2, random_select=0.05, mutate=0.01):
        graded = [(fitness(x, target), x) for x in pop]
        graded = [x[1] for x in sorted(graded)]
        retain_length = int(len(graded)*retain)
        parents = graded[:retain_length]

        # randomly add other individuals to promote genetic diversity
        for individual in graded[retain_length:]:
            if random_select > random():
                parents.append(individual)

        # mutate some individuals
        for individual in parents:
            if mutate > random():
                pos_to_mutate = randint(0, len(individual)-1)
                # this mutation is not ideal, because it
                # restricts the range of possible values,
                # but the function is unaware of the min/max
                # values used to create the individuals,
                individual[pos_to_mutate] = randint(
                    min(individual), max(individual))

        # crossover parents to create children
        parents_length = len(parents)
        desired_length = len(pop) - parents_length
        children = []
        while len(children) < desired_length:
            male = randint(0, parents_length-1)
            female = randint(0, parents_length-1)
            if male != female:
                male = parents[male]
                female = parents[female]
                half = len(male) / 2
                child = male[:half] + female[half:]
                children.append(child)

        parents.extend(children)
        return parents

    target = 371
    p_count = 100
    i_length = 5
    i_min = 0
    i_max = 100
    p = population(p_count, i_length, i_min, i_max)
    fitness_history = [grade(p, target),]
    for i in range(100):
        p = evolve(p, target)
        fitness_history.append(grade(p, target))

    for datum in fitness_history:
       print(datum)

I am following this website http://lethain.com/genetic-algorithms-cool-name-damn-simple/. It was written for Python 2.6 so it does not work for 3. I have updated it mostly but can't get it to work.

Upvotes: 0

Views: 683

Answers (1)

Rob Foley
Rob Foley

Reputation: 581

The errors that the code caused should have been informative enough. The slicing done by:

male[:half] + female[half:] 

Was using half, which was a float at the time. The primary difference was:

half = int(len(male) / 2)

Which is likely the intended functionality. You cannot use floats to index an array, only ints.

Here is what it should be:

from random import randint, random
from functools import reduce
from operator import add
def individual(length, min, max):
    'Create a member of the population.'
    return [randint(min,max) for x in range(length)]


def population(count, length, min, max):
    'Create a number of individuals (i.e. a population).'
    return [ individual(length, min, max) for x in range(count) ]


def fitness(individual, target):
    'Determine the fitness of an individual. Lower is better.'
    sum = reduce(add, individual, 0)
    return abs(target-sum)


def grade(pop, target):
    'Find average fitness for a population.'
    summed = reduce(add, (fitness(x, target) for x in pop), 0)
    return summed / (len(pop) * 1.0)


def evolve(pop, target, retain=0.2, random_select=0.05, mutate=0.01):
    graded = [(fitness(x, target), x) for x in pop]
    graded = [x[1] for x in sorted(graded)]
    retain_length = int(len(graded)*retain)
    parents = graded[:retain_length]

# randomly add other individuals to promote genetic diversity
for individual in graded[retain_length:]:
    if random_select > random():
        parents.append(individual)

# mutate some individuals
for individual in parents:
    if mutate > random():
        pos_to_mutate = randint(0, len(individual)-1)
        # this mutation is not ideal, because it
        # restricts the range of possible values,
        # but the function is unaware of the min/max
        # values used to create the individuals,
        individual[pos_to_mutate] = randint(
            min(individual), max(individual))

# crossover parents to create children
parents_length = len(parents)
desired_length = len(pop) - parents_length
children = []
while len(children) < desired_length:
    male = randint(0, parents_length-1)
    female = randint(0, parents_length-1)
    if male != female:
        male = parents[male]
        female = parents[female]
        half = int(len(male) / 2)
        child = male[:half] + female[half:]
        children.append(child)

parents.extend(children)
return parents

target = 371
p_count = 100
i_length = 5
i_min = 0
i_max = 100
p = population(p_count, i_length, i_min, i_max)
fitness_history = [grade(p, target),]
chance_to_mutate = 0.01
for i in p:
    if chance_to_mutate > random():
        place_to_modify = randint(0,len(i))
        i[place_to_modify] = randint(min(i), max(i))
for i in range(100):
    p = evolve(p, target)
    fitness_history.append(grade(p, target))

for datum in fitness_history:
   print(datum)

Upvotes: 3

Related Questions