def main(numIn, numOut, numGen, popSize, weight_mutpb, con_mutpb, node_mutpb, cxpb): # always maintain a global state of all existing connections innov nums globalInnovation = ((numIn + 1) * numOut) + 1 pop = [] for loop in range(popSize): pop.append(Genotype(numIn, numOut)) ### ***** MAIN EA LOOP ***** ### for g in range(numGen): print("RUNNING GENERATION " + str(g)) # evaluate function handles speciation of population if(g == 145): visHiddenNodes(pop) THRESHOLD = 3.0 THETA1 = 1.0 THETA2 = 1.0 THETA3 = 0.4 tournSize = 3 evaluationTup = evaluateFitness_nichecount(pop, THRESHOLD, THETA1, THETA2, THETA3, g) species = evaluationTup[0] print(len(species)) AVERAGE_FITNESSES.append(evaluationTup[1]) #print(len(species)) popTup = getFittestFromSpecies(species) partialPop = popTup[0] pop = popTup[1] pop = binarySelect(pop, partialPop) # always apply mutation and crossover after selection applyWeightMutation(pop, weight_mutpb) globalInnovation = applyConMutation(pop, con_mutpb, globalInnovation) globalInnovation = applyNodeMutation(pop, node_mutpb, globalInnovation) #pop = applyCrossover(pop, cxpb) # return the resultant population after evolution done return pop#findFittest(pop)
def main(nGen, weightMutpb, nodeMutpb, conMutpb, cxPb, actMutpb, thresh, alpha, theta1, theta2, theta3, numIn, numOut): pop = toolbox.population() # use global innovation object to track the creation of new innovation numbers during evolution gb = GlobalInnovation(numIn, numOut) # the following variables are used to track the improvement of species over generations # if a species' fitness becomes stagnant - it is penalized MIN_NUM_STAGNANT_GENERATIONS = 35 STAGNATION_THRESHOLD = 1.05 LAST_FITNESS = [] CURRENT_STAG_GENS = [] # the following is used for modifying the speciation threshold GENERATION_TO_MODIFY_THRESH = 30 # this is the first generation that the threshold can begin being adjusted DESIRED_NUM_SPECIES = 5 THRESH_MOD = .1 LAST_NUM_SPECIES = -1 for g in range(NGEN): print("RUNNING GENERATION " + str(g)) # use the following conditional to visualize certain properties of population near end of evolution if(g == NGEN - 1): visConnections(pop) visHiddenNodes(pop) # create a 2D array representing species from the population if(g == 0): species = speciatePopulationFirstTime(pop, thresh, theta1, theta2, theta3) else: species = speciatePopulationNotFirstTime(pop, thresh, theta1, theta2, theta3) # determine if speciation threshold needs to be modified and apply modification if(g >= GENERATION_TO_MODIFY_THRESH): numSpecies = len(species) # increase threshold if there are too many species and the number is still increasing if(numSpecies > DESIRED_NUM_SPECIES): if(LAST_NUM_SPECIES == -1 or numSpecies > LAST_NUM_SPECIES): thresh += THRESH_MOD # decrease theshold if there are too many species and the number of species is not increasing elif(numSpecies < DESIRED_NUM_SPECIES): if(LAST_NUM_SPECIES == -1 or numSpecies <= LAST_NUM_SPECIES): thresh -= (THRESH_MOD/2.0) # find all fitness values for individuals in population, update fitness tracking for species for specInd in range(len(species)): avgSpecFit = 0.0 fitnesses = toolbox.map(toolbox.evaluate, species[specInd]) org_index = 0 for fit in fitnesses: # actual fitness value must be divided by the number of individuals in a given species # this keeps any given species from taking over a population - speciation fosters diversity fitness = fit[0]/len(species[specInd]) avgSpecFit += fitness species[specInd][org_index].fit_obj.values = (fitness,) species[specInd][org_index].fitness = fitness org_index += 1 # must find average fitness of species to compare against previous generation and see if species is stagnant avgSpecFit /= len(species[specInd]) # check if fitness is stagnant for current generations and update stagnant counter appropriately if(specInd < len(LAST_FITNESS)): if(avgSpecFit/LAST_FITNESS[specInd] <= STAGNATION_THRESHOLD): CURRENT_STAG_GENS[specInd] = CURRENT_STAG_GENS[specInd] + 1 else: # reset stagnation counter is a species improves enough to be above the threshold CURRENT_STAG_GENS[specInd] = 0 # if this is the first generation for a species, append values for it into both stagnation-tracking lists else: LAST_FITNESS.append(avgSpecFit) CURRENT_STAG_GENS.append(0) # traverse the list of stagnance counters to see if any species need to be penalized for being stagnant index = 0 for spec in CURRENT_STAG_GENS: # if stagnant generations too high, penalize the species if(spec >= MIN_NUM_STAGNANT_GENERATIONS): # penalizing stagnant species for org in species[index]: # penalization increases as the number of stagnant generations increases org.fitness /= (float(2*spec)/MIN_NUM_STAGNANT_GENERATIONS) org.fit_obj.values = (org.fitness,) index += 1 tournamentSelectSpecies = [] # speciate the population after finding corresponding fitnesses print("Num Species: " + str(len(species))) # go through each species and select the best individuals from each species for specInd in range(len(species)): # set all species back to 0 first: for org in species[specInd]: org.species = sys.maxsize bestInd = toolbox.tournSelect(species[specInd], tournsize = 2, k = 1)[0] bestInd = bestInd.getCopy() tournamentSelectSpecies.append(bestInd) # fittest from species function selects all species representatives # and sets the species variable for the rest of the population to sys.maxsize fitTup = getFittestFromSpecies(species) bestInSpecies = fitTup[0] pop = fitTup[1] for org in tournamentSelectSpecies: bestInSpecies.append(org) # select from rest of population to form the full sized population pop = toolbox.select(pop, bestInSpecies) # only apply mutation if there will be another iteration of selection following this if(g < NGEN - 1): # apply weight mutations for ind in pop: if(ind.species == sys.maxsize and np.random.uniform() <= weightMutpb): toolbox.weightMutate(ind) # must invalidate individuals fitness if mutation applied del ind.fit_obj.values # apply node mutations for ind in pop: if(ind.species == sys.maxsize and np.random.uniform() <= nodeMutpb): toolbox.nodeMutate(ind, gb) del ind.fit_obj.values # apply connection mutations for ind in pop: if(ind.species == sys.maxsize and np.random.uniform() <= conMutpb): toolbox.connectionMutate(ind, gb) del ind.fit_obj.values # apply crossover # go through population looking at every pair of individuals next to each other for child1Ind, child2Ind in zip(range(0,len(pop),2), range(1,len(pop),2)): interspecies_probability = .001 # probability individuals crossed over if not in same species child1 = pop[child1Ind] child2 = pop[child2Ind] dist = child1.getDistance(child2, theta1, theta2, theta3) # crossover happens with different probability depending if individuals in question are in same species if(child1.species == sys.maxsize and child2.species == sys.maxsize and dist < thresh and np.random.uniform() <= cxPb): # cross individuals over and put them into the population xTup = toolbox.mate(child1, child2) pop[child1Ind] = xTup[0] pop[child2Ind] = xTup[1] del pop[child1Ind].fit_obj.values del pop[child2Ind].fit_obj.values elif(child1.species == sys.maxsize and child2.species == sys.maxsize and np.random.uniform() <= interspecies_probability): xTup = toolbox.mate(child1, child2) pop[child1Ind] = xTup[0] pop[child2Ind] = xTup[1] del pop[child1Ind].fit_obj.values del pop[child2Ind].fit_obj.values # apply activation mutation for ind in pop: if(ind.species == sys.maxsize and np.random.uniform() <= actMutpb): toolbox.activationMutate(ind) del ind.fit_obj.values # must clear the dictionary of innovation numbers for the coming generation # only check to see if same innovation occurs twice in a single generation gb.clearDict() # return the population after it has been evolved return pop
def main(nGen, weightMutpb, nodeMutpb, conMutpb, cxPb, actMutpb, thresh, alpha, theta1, theta2, theta3, numIn, numOut): pop = toolbox.population() # use global innovation object to track the creation of new innovation numbers during evolution gb = GlobalInnovation(numIn, numOut) # used to check whether a species fitness becomes stagnant LAST_FITNESS = [] CURRENT_STAG_GENS = [] for g in range(NGEN): print("RUNNING GENERATION " + str(g)) # use the following conditional to visualize certain properties of population near end of evolution #if(g == NGEN - 1): # visConnections(pop) # visHiddenNodes(pop) # create a 2D array representing species from the population if (g == 0): species = speciatePopulationFirstTime(pop, thresh, theta1, theta2, theta3) else: species = speciatePopulationNotFirstTime(pop, thresh, theta1, theta2, theta3) # determine if speciation threshold needs to be modified and apply modification # decrease threshold slowly to increase species, but increase quickly to keep to many # species from forming - thus the terms being different sizes if (g >= GENERATION_TO_MODIFY_THRESH): numSpecies = len(species) # increase threshold if there are too many species and the number is still increasing if (numSpecies > DESIRED_NUM_SPECIES): if (LAST_NUM_SPECIES == -1 or numSpecies > LAST_NUM_SPECIES): thresh += THRESH_MOD * 2.0 # decrease theshold if there are too many species and the number of species is not increasing elif (numSpecies < DESIRED_NUM_SPECIES): if (LAST_NUM_SPECIES == -1 or numSpecies <= LAST_NUM_SPECIES): thresh -= (THRESH_MOD / 2.0) # find all fitness values for individuals in population, update fitness tracking for species for specInd in range(len(species)): avgSpecFit = 0.0 # only the output pixels are mapped back, all evaluation must be done below outputs = toolbox.map(toolbox.evaluate, species[specInd]) output_tups = [] for o in outputs: output_tups.append((o[0], PIXELS, len(species[specInd]), MATERIAL_PENALIZATION_THRESHOLD, MATERIAL_UNPRESENT_PENALIZATION)) # map all outputs to the genotypes with their actual fitness assigned fitnesses = toolbox.map(toolbox.assign_fit, output_tups) org_ind = 0 for f in fitnesses: gen = species[specInd][org_ind] avgSpecFit += f[0] gen.fit_obj.values = f gen.fitness = f[0] org_ind += 1 # must find average fitness of species to compare against previous generation and see if species is stagnant avgSpecFit /= len(species[specInd]) ''' org_ind = 0 for out in outputs: gen = species[specInd][org_ind] out = out[0] # original list is inside of a tuple with the genotype proportion_mat_used = float(np.sum(out))/len(PIXELS) penalization = 1.0 if(proportion_mat_used <= MATERIAL_PENALIZATION_THRESHOLD): penalization = 2.0 * (MATERIAL_PENALIZATION_THRESHOLD / (proportion_mat_used + .001)) # find difference between the two pixel arrays ones_arr = np.ones((1, len(PIXELS))) diff = np.subtract(PIXELS, out) diff[diff>=.5] *= MATERIAL_UNPRESENT_PENALIZATION diff = np.fabs(diff) total_fit = (np.sum(np.subtract(ones_arr, diff)))/(len(species[specInd])*penalization) # actual fitness value must be divided by the number of individuals in a given species # this keeps any given species from taking over a population - speciation fosters diversity avgSpecFit += total_fit gen.fit_obj.values = (total_fit,) gen.fitness = total_fit spec_list.append(gen) org_ind += 1 ''' # check if fitness is stagnant for current generations and update stagnant counter appropriately if (specInd < len(LAST_FITNESS)): if (avgSpecFit / LAST_FITNESS[specInd] <= STAGNATION_THRESHOLD): CURRENT_STAG_GENS[specInd] = CURRENT_STAG_GENS[specInd] + 1 else: # reset stagnation counter is a species improves enough to be above the threshold CURRENT_STAG_GENS[specInd] = 0 # if this is the first generation for a species, append values for it into both stagnation-tracking lists else: LAST_FITNESS.append(avgSpecFit) CURRENT_STAG_GENS.append(0) # traverse the list of stagnance counters to see if any species need to be penalized for being stagnant index = 0 for spec in CURRENT_STAG_GENS: # if stagnant generations too high, penalize the species if (spec >= MIN_NUM_STAGNANT_GENERATIONS): # penalizing stagnant species for org in species[index]: # penalization increases as the number of stagnant generations increases org.fitness /= (float(2 * spec) / MIN_NUM_STAGNANT_GENERATIONS) org.fit_obj.values = (org.fitness, ) index += 1 tournamentSelectSpecies = [] # speciate the population after finding corresponding fitnesses print("Num Species: " + str(len(species))) # go through each species and select the best individuals from each species for specInd in range(len(species)): # set all species back to 0 first: for org in species[specInd]: org.species = sys.maxsize bestInd = toolbox.tournSelect(species[specInd], tournsize=2, k=1)[0] bestInd = bestInd.getCopy() tournamentSelectSpecies.append(bestInd) # fittest from species function selects all species representatives # and sets the species variable for the rest of the population to sys.maxsize fitTup = getFittestFromSpecies(species) bestInSpecies = fitTup[0] pop = fitTup[1] for org in tournamentSelectSpecies: bestInSpecies.append(org) # select from rest of population to form the full sized population pop = toolbox.select(pop, bestInSpecies) # only apply mutation if there will be another iteration of selection following this if (g < NGEN - 1): # apply weight mutations for ind in pop: if (ind.species == sys.maxsize and np.random.uniform() <= weightMutpb): toolbox.weightMutate(ind) # must invalidate individuals fitness if mutation applied del ind.fit_obj.values # apply node mutations for ind in pop: if (ind.species == sys.maxsize and np.random.uniform() <= nodeMutpb): toolbox.nodeMutate(ind, gb) del ind.fit_obj.values # apply connection mutations for ind in pop: if (ind.species == sys.maxsize and np.random.uniform() <= conMutpb): toolbox.connectionMutate(ind, gb) del ind.fit_obj.values # apply crossover # go through population looking at every pair of individuals next to each other for child1Ind, child2Ind in zip(range(0, len(pop), 2), range(1, len(pop), 2)): interspecies_probability = .001 # probability individuals crossed over if not in same species child1 = pop[child1Ind] child2 = pop[child2Ind] dist = child1.getDistance(child2, theta1, theta2, theta3) # crossover happens with different probability depending if individuals in question are in same species if (child1.species == sys.maxsize and child2.species == sys.maxsize and dist < thresh and np.random.uniform() <= cxPb): # cross individuals over and put them into the population xTup = toolbox.mate(child1, child2) pop[child1Ind] = xTup[0] pop[child2Ind] = xTup[1] del pop[child1Ind].fit_obj.values del pop[child2Ind].fit_obj.values elif (child1.species == sys.maxsize and child2.species == sys.maxsize and np.random.uniform() <= interspecies_probability): xTup = toolbox.mate(child1, child2) pop[child1Ind] = xTup[0] pop[child2Ind] = xTup[1] del pop[child1Ind].fit_obj.values del pop[child2Ind].fit_obj.values # apply activation mutation for ind in pop: if (ind.species == sys.maxsize and np.random.uniform() <= actMutpb): toolbox.activationMutate(ind) del ind.fit_obj.values # must clear the dictionary of innovation numbers for the coming generation # only check to see if same innovation occurs twice in a single generation gb.clearDict() # return the population after it has been evolved return pop