/
eaneatGP.py
273 lines (228 loc) · 11.6 KB
/
eaneatGP.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
import random
import copy
import os
from deap import tools
from neat_operators import neatGP
from speciation import ind_specie, species, specie_parents_child
from fitness_sharing import SpeciesPunishment
from ParentSelection import p_selection
from my_operators import avg_nodes
def varOr(population, toolbox, cxpb, mutpb):
assert (cxpb + mutpb) <= 1.0, ("The sum of the crossover and mutation "
"probabilities must be smaller or equal to 1.0.")
new_pop = [toolbox.clone(ind) for ind in population]
offspring = []
for i in range(1, len(new_pop), 2):
new_pop[i-1].off_cx_set(0), new_pop[i].off_cx_set(0)
if random.random() < cxpb and len(ind)>1:
new_pop[i-1].off_cx_set(1)
new_pop[i].off_cx_set(1)
offspring1, offspring2 = toolbox.mate(new_pop[i-1], new_pop[i])
del offspring1.fitness.values
del offspring2.fitness.values
offspring1.bestspecie_set(0), offspring2.bestspecie_set(0)
offspring1.off_cx_set(1), offspring2.off_cx_set(1)
offspring.append(offspring1)
offspring.append(offspring2)
for i in range(len(new_pop)):
if new_pop[i].off_cx_get() != 1:
if random.random() < (cxpb+mutpb): # Apply mutation
offspring1, = toolbox.mutate(new_pop[i])
del offspring1.fitness.values
offspring1.bestspecie_set(0)
offspring1.off_mut_set(1)
offspring.append(offspring1)
if len(offspring) < len(population):
for i in range(len(new_pop)):
if new_pop[i].off_mut_get() != 1 and new_pop[i].off_cx_get() != 1:
offspring1 = copy.deepcopy(new_pop[i])
offspring.append(offspring1)
return offspring
def varAnd(population, toolbox, cxpb, mutpb):
"""Part of an evolutionary algorithm applying only the variation part
(crossover **and** mutation). The modified individuals have their
fitness invalidated. The individuals are cloned so returned population is
independent of the input population.
:param population: A list of individuals to vary.
:param toolbox: A :class:`~deap.base.Toolbox` that contains the evolution
operators.
:param cxpb: The probability of mating two individuals.
:param mutpb: The probability of mutating an individual.
:returns: A list of varied individuals that are independent of their
parents.
The variation goes as follow. First, the parental population
:math:`P_\mathrm{p}` is duplicated using the :meth:`toolbox.clone` method
and the result is put into the offspring population :math:`P_\mathrm{o}`.
A first loop over :math:`P_\mathrm{o}` is executed to mate pairs of consecutive
individuals. According to the crossover probability *cxpb*, the
individuals :math:`\mathbf{x}_i` and :math:`\mathbf{x}_{i+1}` are mated
using the :meth:`toolbox.mate` method. The resulting children
:math:`\mathbf{y}_i` and :math:`\mathbf{y}_{i+1}` replace their respective
parents in :math:`P_\mathrm{o}`. A second loop over the resulting
:math:`P_\mathrm{o}` is executed to mutate every individual with a
probability *mutpb*. When an individual is mutated it replaces its not
mutated version in :math:`P_\mathrm{o}`. The resulting
:math:`P_\mathrm{o}` is returned.
This variation is named *And* beceause of its propention to apply both
crossover and mutation on the individuals. Note that both operators are
not applied systematicaly, the resulting individuals can be generated from
crossover only, mutation only, crossover and mutation, and reproduction
according to the given probabilities. Both probabilities should be in
:math:`[0, 1]`.
"""
offspring = [toolbox.clone(ind) for ind in population]
# Apply crossover and mutation on the offspring
for i in range(1, len(offspring), 2):
if random.random() < cxpb:
offspring[i-1], offspring[i] = toolbox.mate(offspring[i-1], offspring[i])
del offspring[i-1].fitness.values, offspring[i].fitness.values
offspring[i-1].bestspecie_set(0), offspring[i].bestspecie_set(0)
for i in range(len(offspring)):
if random.random() < mutpb:
offspring[i], = toolbox.mutate(offspring[i])
del offspring[i].fitness.values
offspring[i].bestspecie_set(0)
return offspring
def ensure_dir(f):
d = os.path.dirname(f)
if not os.path.exists(d):
os.makedirs(d)
def neat_GP( population, toolbox, cxpb, mutpb, ngen, neat_alg, neat_cx, neat_h, neat_pelit, n_corr, num_p, params, problem, beta,stats=None,
halloffame=None, verbose=__debug__):
"""This algorithm reproduce the simplest evolutionary algorithm as
presented in chapter 7 of [Back2000]_.
:param population: A list of individuals.
:param toolbox: A :class:`~deap.base.Toolbox` that contains the evolution
operators.
:param cxpb: The probability of mating two individuals.
:param mutpb: The probability of mutating an individual.
:param ngen: The number of generation.
:param neat_alg: wheter or not to use species stuff.
:param neat_cx: wheter or not to use neatGP cx
:param neat_h: indicate the distance allowed between each specie
:param neat_pelit: probability of being elitist, it's used in the neat cx and mutation
:param n_corr: run number just to wirte the txt file
:param num_p: problem number just to wirte the txt file
:param params:indicate the params for the fitness sharing, the diffetent
options are:
-DontPenalize(str): 'best_specie' or 'best_of_each_specie'
-Penalization_method(int):
1.without penalization
2.penalization fitness sharing
3.new penalization
-ShareFitness(str): 'yes' or 'no'
:param problem: (str) name of the problem.
:param stats: A :class:`~deap.tools.Statistics` object that is updated
inplace, optional.
:param halloffame: A :class:`~deap.tools.HallOfFame` object that will
contain the best individuals, optional.
:param verbose: Whether or not to log the statistics.
:returns: The final population.
It uses :math:`\lambda = \kappa = \mu` and goes as follow.
It first initializes the population (:math:`P(0)`) by evaluating
every individual presenting an invalid fitness. Then, it enters the
evolution loop that begins by the selection of the :math:`P(g+1)`
population. Then the crossover operator is applied on a proportion of
:math:`P(g+1)` according to the *cxpb* probability, the resulting and the
untouched individuals are placed in :math:`P'(g+1)`. Thereafter, a
proportion of :math:`P'(g+1)`, determined by *mutpb*, is
mutated and placed in :math:`P''(g+1)`, the untouched individuals are
transferred :math:`P''(g+1)`. Finally, those new individuals are evaluated
and the evolution loop continues until *ngen* generations are completed.
Briefly, the operators are applied in the following order ::
evaluate(population)
for i in range(ngen):
offspring = select(population)
offspring = mate(offspring)
offspring = mutate(offspring)
evaluate(offspring)
population = offspring
This function expects :meth:`toolbox.mate`, :meth:`toolbox.mutate`,
:meth:`toolbox.select` and :meth:`toolbox.evaluate` aliases to be
registered in the toolbox.
.. [Back2000] Back, Fogel and Michalewicz, "Evolutionary Computation 1 :
Basic Algorithms and Operators", 2000.
"""
d = './Results/%s/bestind_%d_%d.txt' % (problem, num_p, n_corr)
ensure_dir(d)
best = open(d, 'w') # save data
d = './Results/%s/bestind_string_%d_%d.txt' % (problem, num_p, n_corr)
ensure_dir(d)
best_st = open(d, 'w')
logbook = tools.Logbook()
logbook.header = ['gen', 'nevals'] + (stats.fields if stats else [])
if neat_alg: # assign specie to each individual on the population
species(population,neat_h, beta)
ind_specie(population)
# Evaluate the individuals with an invalid fitness
invalid_ind = [ind for ind in population if not ind.fitness.valid]
fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
# take the best on the population
best_ind = best_pop(population)
fitnesst_best = toolbox.map(toolbox.evaluate_test, [best_ind])
best_ind.fitness_test.values=fitnesst_best[0]
best.write('\n%s;%s;%s;%s;%s' % (0, best_ind.fitness_test.values[0], best_ind.fitness.values[0], len(best_ind), avg_nodes(population)))
best_st.write('\n%s;%s' % (0, best_ind))
if neat_alg: # applying fitness sharing
SpeciesPunishment(population,params,neat_h)
if halloffame is not None:
halloffame.update(population)
record = stats.compile(population) if stats else {}
logbook.record(gen=0, nevals=len(invalid_ind), **record)
if verbose:
print logbook.stream
# Begin the generational process
for gen in range(1, ngen+1):
best_ind = copy.deepcopy(best_pop(population))
if neat_alg: # select set of parents
parents = p_selection(population, gen)
else:
parents = toolbox.select(population, len(population))
if neat_cx: # applying neat-crossover
n = len(parents)
mut = 1
cx = 1
offspring = neatGP(toolbox, parents, cxpb, mutpb, n, mut, cx, neat_pelit)
else:
offspring = varOr(parents, toolbox, cxpb, mutpb)
if neat_alg: # Assign species
specie_parents_child(parents, offspring, neat_h, beta)
offspring[:] = parents+offspring
ind_specie(offspring)
# Evaluate the individuals with an invalid fitness
invalid_ind = [ind for ind in offspring]
fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
else:
invalid_ind = [ind for ind in offspring]
fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
orderbyfit = sorted(offspring, key=lambda ind: ind.fitness.values)
if best_ind.fitness.values[0] <= orderbyfit[0].fitness.values[0]:
offspring[:] = [best_ind] + orderbyfit[:len(population) - 1]
if neat_alg:
SpeciesPunishment(offspring, params, neat_h)
# Update the hall of fame with the generated individuals
if halloffame is not None:
halloffame.update(offspring)
# Replace the current population by the offspring
population[:] = offspring
# Append the current generation statistics to the logbook
record = stats.compile(population) if stats else {}
logbook.record(gen=gen, nevals=len(population), **record)
if verbose:
print logbook.stream
best_ind = best_pop(population)
fitnesses_test = toolbox.map(toolbox.evaluate_test, [best_ind])
best_ind.fitness_test.values = fitnesses_test[0]
best.write('\n%s;%s;%s;%s;%s'%(gen, best_ind.fitness_test.values[0], best_ind.fitness.values[0], len(best_ind), avg_nodes(population)))
best_st.write('\n%s;%s' % (gen, best_ind))
return population, logbook
def best_pop(population):
orderbyfit = list()
orderbyfit = sorted(population, key=lambda ind:ind.fitness.values)
return orderbyfit[0]