1

I have made a genetic algorithm using DEAP package. My evaluation function looks like the following

Arch = np.array([0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0])

def evalComp(individual):
    compare = ((np.absolute( Arch[3] - individual[3])+np.absolute( Arch[4] - individual[4])+np.absolute( Arch[5] - individual[5])+np.absolute( Arch[6] - individual[6])+np.absolute( Arch[7] - individual[7])+np.absolute( Arch[8] - individual[8])+np.absolute( Arch[9] - individual[9])+np.absolute( Arch[10] - individual[10])+np.absolute( Arch[11] - individual[11])+np.absolute( Arch[12] - individual[12])+np.absolute( Arch[13] - individual[13])+np.absolute( Arch[14] - individual[14])+np.absolute( Arch[15] - individual[15])+np.absolute( Arch[16] - individual[16])+np.absolute( Arch[17] - individual[17])+np.absolute( Arch[18] - individual[18])+np.absolute( Arch[19] - individual[19]))/17)
    return compare,

What I would like to do is change "Arch" to a different numpy array after each generation. My problem is "g" is defined by the main function and "evalComp" is outside the main function. Here is the full GA:

import random
import time
from deap import base
from deap import creator
from deap import tools
import numpy as np
from secrets import randbelow

start_time = time.time() # Calculates runtime
creator.create("FitnessMax", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)

toolbox = base.Toolbox()
toolbox.register("attr_int", randbelow, (10))
toolbox.register("individual", tools.initRepeat, creator.Individual, 
                 toolbox.attr_int, 20) 

toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# Archetype

Arch = np.array([0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0])

pop = toolbox.population(n=5)
pop = (pop)
print(pop)
print("\n")
print("\n")

def evalComp(individual):
    compare = ((np.absolute( Arch[3] - individual[3])+np.absolute( Arch[4] - individual[4])+np.absolute( Arch[5] - individual[5])+np.absolute( Arch[6] - individual[6])+np.absolute( Arch[7] - individual[7])+np.absolute( Arch[8] - individual[8])+np.absolute( Arch[9] - individual[9])+np.absolute( Arch[10] - individual[10])+np.absolute( Arch[11] - individual[11])+np.absolute( Arch[12] - individual[12])+np.absolute( Arch[13] - individual[13])+np.absolute( Arch[14] - individual[14])+np.absolute( Arch[15] - individual[15])+np.absolute( Arch[16] - individual[16])+np.absolute( Arch[17] - individual[17])+np.absolute( Arch[18] - individual[18])+np.absolute( Arch[19] - individual[19]))/17)
    return compare,

toolbox.register("evaluate", evalComp)
toolbox.register("mate", tools.cxUniform)
toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=4)

def main():
    random.seed(43)
    pop = toolbox.population(n=50)


    print("Start of evolution")

    fitnesses = list(map(toolbox.evaluate, pop))
    for ind, fit in zip(pop, fitnesses):
        ind.fitness.values = fit

    print(" Evaluated %i individuals" % len(pop))

    CXPB, MUTPB = 0.5, 0.3
    fits = [ind.fitness.values[0] for ind in pop]
    global g
    g = 0
    while max(fits) > 0 and g < 50:
        g = g + 1
        print("-- Generation %i --" % g)
        offspring = toolbox.select(pop, len(pop))
        offspring = list(map(toolbox.clone, offspring))
        for child1, child2 in zip(offspring[::2], offspring[1::2]):
            if random.random() < CXPB:
                toolbox.mate(child1, child2, 0.3)
                del child1.fitness.values
                del child2.fitness.values

        for mutant in offspring:
            if random.random() < MUTPB:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        print(" Evaluated %i individuals" % len(invalid_ind))

        pop[:] = offspring
        fits = [ind.fitness.values[0] for ind in pop]

        length = len(pop)
        mean = sum(fits) / length
        sum2 = sum(x*x for x in fits)
        std = abs(sum2 / length - mean**2)**0.5

        print(" Min %s" % min(fits))
        print(" Max %s" % max(fits))
        print(" Avg %s" % mean)
        print(" Std %s" % std)

    print("-- End of (successful) evolution --")
    print("\n")
    print("\n")
    print("Archetype",  Arch)

    print("\n")
    best_ind = tools.selBest(pop, 1)[0]
    print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values))

if __name__ == "__main__":
    main()


print("--- %s seconds ---" % (time.time() - start_time))

Thanks in advance!!

UPDATE:

@usernumber has suggested defining Arch inside the generation loop. I have attempted this and it seems to print the new Arch after each loop, however the evalComp still uses the original definition of Arch to calculate fitness. If I remove the original Arch = np.array([...]) line, I get NameError: name 'Arch' is not defined.

What I want is for Arch from inside the generation loop to be used in the evaluation function:evalComp().

badam
  • 55
  • 4

1 Answers1

1

Add Arch to the parameters of your evaluation function

def evalComp(individual, Arch):
    compare = ...
    return compare,

Then when you register the evaluation function in the toolbox, pass Arch to it

toolbox.register("evaluate", evalComp, Arch)

Since Arch points to the numpy array, when you modify its contents, eval will access the new contents of the array. So in the generation loop, you just have to set the value of Arch to whatever you want it to be

while g<50:
    Arch = np.random.rand(10)

Full code then becomes

import random
import time
from deap import base
from deap import creator
from deap import tools
import numpy as np
from secrets import randbelow

start_time = time.time() # Calculates runtime
creator.create("FitnessMax", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)

toolbox = base.Toolbox()
toolbox.register("attr_int", randbelow, (10))
toolbox.register("individual", tools.initRepeat, creator.Individual, 
                 toolbox.attr_int, 20) 

toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# Archetype

Arch = np.array([0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0])

pop = toolbox.population(n=5)
pop = (pop)
print(pop)
print("\n")
print("\n")

def evalComp(individual, Arch):
    compare = ((np.absolute( Arch[3] - individual[3])+np.absolute( Arch[4] - individual[4])+np.absolute( Arch[5] - individual[5])+np.absolute( Arch[6] - individual[6])+np.absolute( Arch[7] - individual[7])+np.absolute( Arch[8] - individual[8])+np.absolute( Arch[9] - individual[9])+np.absolute( Arch[10] - individual[10])+np.absolute( Arch[11] - individual[11])+np.absolute( Arch[12] - individual[12])+np.absolute( Arch[13] - individual[13])+np.absolute( Arch[14] - individual[14])+np.absolute( Arch[15] - individual[15])+np.absolute( Arch[16] - individual[16])+np.absolute( Arch[17] - individual[17])+np.absolute( Arch[18] - individual[18])+np.absolute( Arch[19] - individual[19]))/17)
    return compare,

toolbox.register("evaluate", evalComp, Arch)
toolbox.register("mate", tools.cxUniform)
toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=4)

def main():
    random.seed(43)
    pop = toolbox.population(n=50)


    print("Start of evolution")

    fitnesses = list(map(toolbox.evaluate, pop))
    for ind, fit in zip(pop, fitnesses):
        ind.fitness.values = fit

    print(" Evaluated %i individuals" % len(pop))

    CXPB, MUTPB = 0.5, 0.3
    fits = [ind.fitness.values[0] for ind in pop]
    global g
    g = 0
    while max(fits) > 0 and g < 50:
        g = g + 1
        Arch = np.random.rand(20)
        print("-- Generation %i --" % g)
        offspring = toolbox.select(pop, len(pop))
        offspring = list(map(toolbox.clone, offspring))
        for child1, child2 in zip(offspring[::2], offspring[1::2]):
            if random.random() < CXPB:
                toolbox.mate(child1, child2, 0.3)
                del child1.fitness.values
                del child2.fitness.values

        for mutant in offspring:
            if random.random() < MUTPB:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        print(" Evaluated %i individuals" % len(invalid_ind))

        pop[:] = offspring
        fits = [ind.fitness.values[0] for ind in pop]

        length = len(pop)
        mean = sum(fits) / length
        sum2 = sum(x*x for x in fits)
        std = abs(sum2 / length - mean**2)**0.5

        print(" Min %s" % min(fits))
        print(" Max %s" % max(fits))
        print(" Avg %s" % mean)
        print(" Std %s" % std)

    print("-- End of (successful) evolution --")
    print("\n")
    print("\n")
    print("Archetype",  Arch)

    print("\n")
    best_ind = tools.selBest(pop, 1)[0]
    print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values))

if __name__ == "__main__":
    main()


print("--- %s seconds ---" % (time.time() - start_time))
usernumber
  • 1,958
  • 1
  • 21
  • 58
  • If I do this, how can I get the contents of the array to update after each generation. For example: for generation 1 I want ```Arch = np.array([0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0])```, for generation 2 I want ```Arch = np.array([0,0,0,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2])```, for generation 3 I want ```Arch = np.array([0,0,0,5,3,1,4,5,4,5,4,5,4,5,4,5,4,5,4,5])```etc. Arch should update to a different array after each loop. – badam Dec 03 '19 at 00:08
  • 1
    In your generation loop (`while g<50`), add a line to update `Arch`. – usernumber Dec 03 '19 at 09:02
  • @badam I can't parse python written without line breaks. See updated answer. – usernumber Dec 03 '19 at 15:43
  • Sorry about the formating (I'm new at this) Your code seems to work however I do need Arch to be a specific np.array for each generation, not a random one. I attempted an `if` `elif` statement in the generation loop but could not get it to work. ```if g < 5: Arch = np.array([0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0])``` ```elif g < 10:...``` – badam Dec 03 '19 at 16:19
  • You can replace the line `Arch = np.random.rand(20)` by whatever you need `Arch` to be. I feel like this is a different question you are asking now. – usernumber Dec 03 '19 at 16:39
  • Also, I just realized it keeps using the original definition of ```Arch``` that is outside ```def main()``` to do the comparison in ```def evalComp(individual, Arch): ...```. I guess this is why when I change ```Arch``` within the loop it has no effect on the output. – badam Dec 03 '19 at 16:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/203554/discussion-between-badam-and-usernumber). – badam Dec 03 '19 at 19:55