Hi Taavi
Thank you, I'll take a look at the qdpy library, sounds cool with the container!
I have tried to add the filter and store the intermediate solutions. However, i don't get any feasible solutions, although, I know that some exist. I suspect this is because the model conerge towards the optimised indicator, without checking for feasibility, and thus the solutions that I store do not cover the feasible solutions. Running more than the iterations and individuals than I do now (i.e. 1000 * 500) already takes quite a long time. Do you know if there is a smart way to easily delete infeasible solutions everytime an individual is evaluated?
What I have tried so far is adding a feasibility function:
----------------------------------------------------------------------------------
def feasible(individual):
individual = individual[0]
if (
#'Nutritional constraints'
7*1800 < sum(x*y for x,y in zip(list(adict['Calories']),individual)) < 7*2200 and # and
7*50 < sum(x*y for x,y in zip(list(adict['Protein']),individual)) and
.............and so on.................
):
return True
return False
----------------------------------------------------------------------------------
And then I have run the model and filtered by the feasible solutions:
----------------------------------------------------------------------------------
pop = toolbox.population(n=1000)
# Evaluate the entire population
fitnesses = list(map(toolbox.evaluate, pop))
for ind, fit in zip(pop, fitnesses): # individual and fit value in population
ind.fitness.values = fit
# CXPB is the probability with which two individuals
# are crossed
#
# MUTPB is the probability for mutating an individual
CXPB, MUTPB = 0.8, 0.3
# Extracting all the fitnesses of
fits = [ind.fitness.values[0] for ind in pop]
# Variable keeping track of the number of generations
g = 0
# check feasible solution in initial population:
pop_init_valid = list(filter(feasible,pop))
# make empty df to store feasible solutions at intermediate steps
pop_all = pd.DataFrame()
pop_all = pop_all.append(pop_init_valid)
fits_all = pd.DataFrame()
# Begin the evolution
while g < 500:
# A new generation
g = g + 1
#print("-- Generation %i --" % g)
# Select the next generation individuals
offspring = toolbox.select(pop, len(pop))
#offspring = list(filter(feasible,offspring))
# Clone the selected individuals
offspring = list(map(toolbox.clone, offspring))
# Apply crossover and mutation on the offspring
for child1, child2 in zip(offspring[::2], offspring[1::2]):
if random.random() < CXPB:
toolbox.mate(child1[0], child2[0])
del child1.fitness.values
del child2.fitness.values
for mutant in offspring:
if random.random() < MUTPB:
toolbox.mutate(mutant[0])
del mutant.fitness.values
#valid_ind = [ind for ind in offspring if ind.fitness.valid]
# Evaluate the individuals with an invalid fitness
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
pop[:] = offspring
# Gather all the fitnesses in one list and print the stats
fits = [ind.fitness.values[0] for ind in pop]
pop_all = pop_all.append(list(filter(feasible,pop)))
----------------------------------------------------------------------------------
This is probably what the qdpy could take of (?), but it might be that there were a way to do it without :)
Best
Caroline