-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathpopbuild.m
307 lines (236 loc) · 11.1 KB
/
popbuild.m
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
function gp = popbuild(gp)
%POPBUILD Build next population of individuals.
%
% GP = POPBUILD(GP) uses the current population (stored in GP.POP) and
% their fitnesses (stored in GP.FITNESS.VALUES) and, optionally,
% complexities to create the next generation of individuals.
%
% Copyright (c) 2009-2015 Dominic Searson
%
% GPTIPS 2
%
% See also INITBUILD
%initialise new population
newPop = cell(gp.runcontrol.pop_size,1);
%the number of new members to be constructed after elitism is accounted for
num2build = floor( (1 - gp.selection.elite_fraction) * gp.runcontrol.pop_size);
num2skim = gp.runcontrol.pop_size - num2build;
%parameter shortcuts
p_mutate = gp.operators.mutation.p_mutate;
p_direct = gp.operators.directrepro.p_direct;
maxDepth = gp.treedef.max_depth;
max_nodes = gp.treedef.max_nodes;
p_cross_hi = gp.genes.operators.p_cross_hi;
crossRate = gp.genes.operators.hi_cross_rate;
useMultiGene = gp.genes.multigene;
pmd = p_mutate + p_direct;
maxNodesInf = isinf(max_nodes);
maxGenes = gp.genes.max_genes;
%reset cache
if gp.runcontrol.usecache
remove(gp.fitness.cache,gp.fitness.cache.keys);
end
%update gen counter
gp.state.count = gp.state.count + 1;
buildCount = 0;
%loop until the required number of new individuals has been built.
while buildCount < num2build;
buildCount = buildCount + 1;
%probabilistically select a genetic operator
p_gen = rand;
if p_gen < p_mutate %select mutation
eventType = 1;
elseif p_gen < pmd %direct reproduction
eventType = 2;
else %crossover
eventType = 3;
end
%mutation (first select a individual, then a gene, then do ordinary mutate)
if eventType == 1
parentIndex = selection(gp); %pick the population index of a parent individual using selection operator
parent = gp.pop{parentIndex};
if useMultiGene %if using multigene, extract a target gene
numParentGenes = numel(parent);
targetGeneIndex = ceil(rand * numParentGenes); %randomly select a gene from parent
targetGene = parent{1,targetGeneIndex}; %extract it
else
targetGeneIndex = 1;
targetGene = parent{1};
end
mutateSuccess = false;
for loop = 1:10 %loop until a successful mutation occurs (max loops=10)
mutatedGene = mutate(targetGene,gp);
mutatedGeneDepth = getdepth(mutatedGene);
if mutatedGeneDepth <= maxDepth
if maxNodesInf
mutateSuccess = true;
break;
end
mutatedGeneNodes = getnumnodes(mutatedGene);
if mutatedGeneNodes <= max_nodes
mutateSuccess = true;
break;
end
end %end of constraint check
end %end of mutate for loop
%if no success then use parent gene
if ~mutateSuccess
mutatedGene = targetGene;
end
%add the mutated individual to new pop
parent{1,targetGeneIndex} = mutatedGene;
newPop{buildCount,1} = parent;
%direct reproduction operator
elseif eventType == 2
parentIndex = selection(gp); %pick a parent
parent = gp.pop{parentIndex};
%copy to new population
newPop{buildCount} = parent;
%store fitness etc of copied individual if cache enabled
if gp.runcontrol.usecache
cachedData.complexity = gp.fitness.complexity(parentIndex,1);
cachedData.returnvalues = gp.fitness.returnvalues{parentIndex,1};
cachedData.value = gp.fitness.values(parentIndex,1);
gp.fitness.cache(buildCount) = cachedData;
end
%crossover operator - can either pick 'high level' crossover
%(crosses over entire genes with no tree alteration) or 'low level'
% which crosses over individual genes at the tree level.
elseif eventType == 3
highLevelCross = false;
if useMultiGene
%select crossover type if multigene enabled
if rand < p_cross_hi
highLevelCross = true;
end
end
%Select Parents
parentIndex = selection(gp);
dad = gp.pop{parentIndex};
numDadGenes = numel(dad);
parentIndex = selection(gp);
mum = gp.pop{parentIndex};
numMumGenes = numel(mum);
if highLevelCross
if numMumGenes>1 || numDadGenes>1
%high level crossover (only use if either parent has more
%than one gene) This is modified/simplified from the
%version in GPTIPS v1. This version just chooses between 1
%and N genes to exchange randomly and independently for
%each parent with a probability set by
%gp.genes.operators.hi_cross_rate (where N = number of
%genes in that individual). Like GPTIPS 1, this assumes
%that the order of genes within an individual is not
%important.
dadGeneSelectionInds = rand(1,numDadGenes) < crossRate;
mumGeneSelectionInds = rand(1,numMumGenes) < crossRate;
if ~any(dadGeneSelectionInds)
dadGeneSelectionInds(1,ceil(numDadGenes *rand)) = true;
end
if ~any(mumGeneSelectionInds)
mumGeneSelectionInds(1,ceil(numMumGenes *rand)) = true;
end
dadSelectedGenes = dad(dadGeneSelectionInds);
mumSelectedGenes = mum(mumGeneSelectionInds);
dadRemainingGenes = dad(~dadGeneSelectionInds);
mumRemainingGenes = mum(~mumGeneSelectionInds);
mumOffspring = [mumRemainingGenes dadSelectedGenes];
dadOffspring = [dadRemainingGenes mumSelectedGenes];
%curtail offspring longer than the max allowed number of genes
%before writing to new population (only write 1 if no space for 2 offspring)
newPop{buildCount,1} = mumOffspring(1:(min(end,maxGenes)));
buildCount = buildCount+1;
if buildCount <= num2build
newPop{buildCount,1} = dadOffspring(1:(min(end,maxGenes)));
end
else
highLevelCross = false;
end
end
%low level crossover: picks a random gene from each parent and
%crosses them. The offspring replace the original target genes
if ~highLevelCross
if useMultiGene %if multigene then select target genes
dad_target_gene_num = ceil(rand*numDadGenes); %randomly select a gene from dad
mum_target_gene_num = ceil(rand*numMumGenes); %randomly select a gene from mum
dad_target_gene = dad{1,dad_target_gene_num};
mum_target_gene = mum{1,mum_target_gene_num};
else
dad_target_gene_num = 1;
mum_target_gene_num = 1;
dad_target_gene = dad{1};
mum_target_gene = mum{1};
end
for loop = 1:10 %loop (max 10 times) until both children meet size constraints
%produce 2 offspring
[son,daughter] = crossover(mum_target_gene,dad_target_gene,gp);
son_depth = getdepth(son);
%check if both children meet size and depth constraints
%if true then break out of while loop and proceed
crossOverSuccess = false;
if son_depth <= maxDepth
daughter_depth = getdepth(daughter);
if daughter_depth <= maxDepth
if maxNodesInf
crossOverSuccess = true;
break;
end
son_nodes = getnumnodes(son);
if son_nodes <= max_nodes
daughter_nodes = getnumnodes(daughter);
if daughter_nodes <= max_nodes
crossOverSuccess = true;
break;
end;
end
end
end
end
%if no success then re-insert parents
if ~crossOverSuccess
son = dad_target_gene;
daughter = mum_target_gene;
end
%write offspring back to right gene positions in parents and write to population
dad{1,dad_target_gene_num} = son;
newPop{buildCount} = dad;
buildCount = buildCount+1;
if buildCount <= num2build
mum{1,mum_target_gene_num} = daughter;
newPop{buildCount} = mum;
end
end %end of if ~use_high
end % end of op_type if
end %end of ii-->num2build for
%skim off the existing elite individuals and stick them on the end of the
%new population. When sorting there may be many individuals with the same
%fitness. However, some of these have fewer nodes/lower complexity than
%others, so skim off the first one as the existing member with the fewest
%nodes. This should exert a degree of parsimony pressure.
%get indices of best num2skim individuals
[~,sortIndex] = sort(gp.fitness.values);
%if maximising need to flip vectors
if ~gp.fitness.minimisation
sortIndex = flipud(sortIndex);
end
for g=1:num2skim
oldIndex = sortIndex(g);
%for the first individual to skim off, pick the 'best' member with the
%lowest complexity
if g==1
bestInds = find( gp.fitness.values == gp.fitness.values(oldIndex) );
[~,oldIndex] = sort(gp.fitness.complexity(bestInds));
oldIndex = bestInds(oldIndex(1)); %if more than one with same complexity just pick the first
end
newIndex = num2build + g;
copiedIndividual = gp.pop{oldIndex,1};
%cache fitness, complexity etc.
if gp.runcontrol.usecache
cachedData.complexity = gp.fitness.complexity(oldIndex,1);
cachedData.returnvalues = gp.fitness.returnvalues{oldIndex,1};
cachedData.value = gp.fitness.values(oldIndex,1);
gp.fitness.cache(newIndex) = cachedData;
end
newPop{newIndex,1} = copiedIndividual;
end
gp.pop = newPop;