1 """
2 evolver
3 =======
4 This module contains the methods to start and finish a complete evolutionary run.
5 The present version can run strongly-typed Koza-based GP using tournament
6 selection.
7
8 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
14 THE SOFTWARE.
15
16 @author: by Mehdi Khoury
17 @version: 1.20
18 @copyright: (c) 2009 Mehdi Khoury under the mit license
19 http://www.opensource.org/licenses/mit-license.html
20 @contact: mehdi.khoury at gmail.com
21
22 """
23
24 import settings
25 import timeit
26 import selection
27 import buildtree
28 import cPickle
29 import marshal
30 import pprint
31
32 import evalfitness
33 from pysqlite2 import dbapi2 as sqlite
34 import crossutil
35 import math
36 import writepop
37 import copy
38 import mutation
39 import psyco
40 import crossover
41 import os
42
43 psyco.profile()
44
48
50
51
52 -def EvolutionRun(popsize,root_node,mindepth,maxdepth,buildmethod,max_nb_runs, fitness_criterion ,crossover_prob,mutation_prob,size, prob_selection,dbname,verbose):
53 """
54
55 Function: EvolutionRun
56 =======================
57 The highest level function of the package.
58 It starts an evolutionary run with given parameters,and gives indications of
59 what is found after each generation.
60 @param popsize: size of the population
61 root_node: specify the root node and its arity (nb of children). e.g. (0,2,'root')
62 @param mindepth: min tree depth (at the moment only 2 working)
63 @param maxdepth: max depth of trees in new generation (should be >=3)
64 @param buildmethod: which Koza method is used to build the trees (either
65 'AddHalfNode' or 'AddFullNode' or 'AddGrowNodeMin' respectively for
66 Ramped Half-n-Half, Full, or Half)
67 @param max_nb_runs: the search will gon on until a maximum number of generations
68 is reached
69 @param fitness_criterion: the search will stop if the fitness found is <= to
70 the ideal fitness
71 @param crossover_prob: probability of crossover (will determine what proportion of
72 the population will be replaced by crossover-generated offsprings)
73 @param mutation_prob: probability of crossover (will determine what proportion of
74 the population will be replaced by mutation-generated offsprings)
75 @param dbname: path to database e.g. r'D:\3d_work\pythongp\pySTGP_0.51\src\pop_db'
76 @param verbose: print the best tree of each generation
77
78 """
79 try:
80 os.remove(dbname)
81 except:
82 pass
83
84 current_best_fitness=float('inf')
85 tablenames=[]
86
87 tablenames.append('tab0')
88 writepop.WriteInitialPopulation2DB(popsize,root_node,mindepth,maxdepth,buildmethod,dbname,tablenames[0])
89
90 db_list=selection.GetDBKeysAndFitness(dbname,tablenames[0])
91 chosen_one=selection.SelectDBOneFittest(db_list)
92 current_best_fitness=chosen_one[1]
93 print ''.join(['generation 0 (db table name = tab0): -> best fit individual has id:',`chosen_one[0]`,' and fitness:',`current_best_fitness`])
94
95 if verbose==True:
96
97 con = sqlite.connect(dbname)
98 SELECT = "select tree from %s where o_id=%d" % (tablenames[0],chosen_one[0])
99 cur = con.cursor()
100 cur.execute(SELECT)
101 con.commit()
102 myresult= cur.fetchone()
103 con.close()
104 best_tree=copy.deepcopy(marshal.loads(myresult[0]))
105 print best_tree
106 if current_best_fitness<=fitness_criterion:
107 print ''.join(['found solution: generation 0, db_id:',`chosen_one[0]`,' and fitness:',`current_best_fitness`])
108 else:
109
110 i=1
111 while i<max_nb_runs and current_best_fitness>fitness_criterion:
112 tablenames.append(''.join(['tab',`i`]))
113 TournamentSelectionEvolveDBPopulation2(popsize,maxdepth,crossover_prob,mutation_prob,size, prob_selection,dbname,tablenames[i-1],tablenames[i])
114 db_list=selection.GetDBKeysAndFitness(dbname,tablenames[i])
115 chosen_one=selection.SelectDBOneFittest(db_list)
116 current_best_fitness=chosen_one[1]
117 print ''.join(['generation ',`i`,' (db table name = tab',`i`,'): -> best fit individual has id:',`chosen_one[0]`,' and fitness:',`current_best_fitness`])
118
119 if verbose==True:
120
121 con = sqlite.connect(dbname)
122 SELECT = "select tree from %s where o_id=%d" % (tablenames[i],chosen_one[0])
123 cur = con.cursor()
124 cur.execute(SELECT)
125 con.commit()
126 myresult= cur.fetchone()
127 con.close()
128 best_tree=copy.deepcopy(marshal.loads(myresult[0]))
129 print best_tree
130
131 i=i+1
132 else:
133 if current_best_fitness<=fitness_criterion:
134 print ''.join(['found solution at generation ',`i-1`,', with fitness:',`current_best_fitness`])
135 writepop.PrintPopFromDB(dbname,tablenames[i-1],'lastpop')
136 else:
137 print ''.join(['Fitness stopping criterion not found. Run ended at generation ',`i`])
138
139
140
141
142
143
145 """
146 Function: TournamentSelectionEvolveDBPopulation2
147 =================================================
148 create a new population of randomly generated trees and write this new generation
149 to a new table of name 'tab'+generation number in the database.
150
151 @param popsize: size of the population
152 @param maxdepth: max depth of trees in new generation
153 @param crossover_prob: probability of crossover (will determine what proportion of
154 the population will be replaced by crossover-generated offsprings)
155 @param mutation_prob: probability of crossover (will determine what proportion of
156 the population will be replaced by mutation-generated offsprings)
157 @param dbname: path to database e.g. r'D:\3d_work\pythongp\pySTGP_0.51\src\pop_db'
158 @param tablename: name of the database table of the initial population
159 @param tablename2: name of the database table of the next generation
160
161 """
162
163 if crossover_prob>1 or crossover_prob<0:
164 raise CrossoverProbError, "Crossover Probability should be in interval [0,1]"
165 exit
166 if mutation_prob>1 or mutation_prob<0:
167 raise MutationProbError, "Crossover Probability should be in interval [0,1]"
168 exit
169 reproduction_prob= 1-(crossover_prob+mutation_prob)
170 if reproduction_prob>1 or reproduction_prob<0:
171 raise OperatorProbError, "Sum of Mutation and Crossover Probability should be in interval [0,1]"
172 exit
173 if popsize<3:
174 raise PopSizeError, "The size of the population must be at least 3"
175 exit
176
177 new_pop=[]
178 trees=[]
179
180
181
182 crossover_size= math.ceil(popsize*crossover_prob)
183 mutation_size= math.ceil(popsize*mutation_prob)
184 reproduction_size= math.ceil(popsize*reproduction_prob)
185 sizes=[crossover_size,mutation_size,reproduction_size]
186 theoretical_size=sum(sizes)
187 if theoretical_size >popsize:
188 nb=theoretical_size-popsize
189 if crossover_size>mutation_size and mutation_size>=reproduction_size:
190 crossover_size=crossover_size-nb
191 elif mutation_size>crossover_size and crossover_size>=reproduction_size:
192 mutation_size=crossover_size-nb
193 elif reproduction_size>crossover_size and crossover_size>=mutation_size:
194 mutation_size=crossover_size-nb
195 else:
196 crossover_size=crossover_size-nb
197
198
199
200
201 db_list=selection.GetDBKeysAndFitness(dbname,tablename)
202
203 reprod=selection.SelectDBSeveralFittest(int(reproduction_size), db_list)
204
205
206 cross=selection.TournamentSelectDBSeveral(int(crossover_size),size, prob_selection,db_list)
207
208
209 mut=selection.TournamentSelectDBSeveral(int(mutation_size),size, prob_selection,db_list)
210
211
212
213
214 con = sqlite.connect(dbname)
215
216
217
218
219
220
221 for elem in reprod:
222 o_id=elem[0]
223
224 SELECT = "select tree, tree_mapping, treedepth, evaluated, fitness from %s where o_id=%d" % (tablename,o_id)
225 cur = con.cursor()
226 cur.execute(SELECT)
227 con.commit()
228 myresult= cur.fetchone()
229 my_tree=copy.deepcopy(marshal.loads(myresult[0]))
230 my_tree_mapping=copy.deepcopy(marshal.loads(myresult[1]))
231 my_treedepth=myresult[2]
232 my_evaluated=myresult[3]
233 my_fitness=myresult[4]
234
235 new_pop.append((o_id,my_tree, my_tree_mapping,my_treedepth,my_evaluated,my_fitness))
236 trees.append(my_tree)
237
238 con.commit()
239
240 for elem in mut:
241 o_id=elem[0]
242
243 SELECT = "select tree, tree_mapping, treedepth, evaluated, fitness from %s where o_id=%d" % (tablename,o_id)
244 cur = con.cursor()
245 cur.execute(SELECT)
246 con.commit()
247 myresult= cur.fetchone()
248 my_tree=copy.deepcopy(marshal.loads(myresult[0]))
249 my_tree_mapping=copy.deepcopy(marshal.loads(myresult[1]))
250 my_treedepth=myresult[2]
251 my_evaluated=myresult[3]
252 my_fitness=myresult[4]
253 same_tree=True
254 mt=mutation.Mutate(maxdepth,my_tree,my_tree_mapping,my_treedepth)
255 same_tree=mt[0]
256 if mt in trees:
257 same_tree=True
258
259 if same_tree==True:
260 while same_tree==True:
261 mt=mutation.Mutate(maxdepth,my_tree,my_tree_mapping,my_treedepth)
262 same_tree=mt[0]
263 mt_map=crossutil.GetIndicesMappingFromTree(mt[1])
264 mt_depth=crossutil.GetDepthFromIndicesMapping(mt_map)
265 mt_evaluated=1
266
267 result_fitness=settings.FitnessFunction(mt[1])
268
269
270
271
272
273
274
275
276 new_pop.append((o_id,mt[1], mt_map,mt_depth,mt_evaluated,result_fitness))
277
278 con.commit()
279
280
281 for elem in cross:
282
283 parent2=selection.TournamentSelectDBSeveral(2,7, 0.8,db_list)
284
285 if elem==parent2[0]:
286 elem2=parent2[1]
287 else:
288 elem2=parent2[0]
289
290
291 o_id=elem[0]
292 o_id2=elem2[0]
293
294
295 cur = con.cursor()
296 SELECT1 = "select tree, tree_mapping, treedepth, evaluated, fitness from %s where o_id=%d" % (tablename,o_id)
297 cur.execute(SELECT1)
298 con.commit()
299 myresult1= cur.fetchone()
300 SELECT2 = "select tree, tree_mapping, treedepth, evaluated, fitness from %s where o_id=%d" % (tablename,o_id2)
301 cur.execute(SELECT2)
302 con.commit()
303 myresult2= cur.fetchone()
304
305 my_tree1=copy.deepcopy(marshal.loads(myresult1[0]))
306 my_tree1_mapping=copy.deepcopy(marshal.loads(myresult1[1]))
307 my_tree1depth=myresult1[2]
308 my_evaluated1=myresult1[3]
309 my_fitness1=myresult1[4]
310
311 my_tree2=copy.deepcopy(marshal.loads(myresult2[0]))
312 my_tree2_mapping=copy.deepcopy(marshal.loads(myresult2[1]))
313 my_tree2depth=myresult2[2]
314 my_evaluated2=myresult2[3]
315 my_fitness2=myresult2[4]
316
317
318
319
320
321
322 cs_evaluated=1
323
324 input_sets=xrange(settings.nb_eval)
325 cs=[[0,0,0,0]]
326 i=0
327 while cs[0]!=[1,1,1,1] and i<100 :
328
329
330
331
332 cp_my_tree1=marshal.loads(myresult1[0])
333 cp_my_tree1_mapping=marshal.loads(myresult1[1])
334 cp_my_tree2=marshal.loads(myresult2[0])
335 cp_my_tree2_mapping=marshal.loads(myresult2[1])
336 cs=crossover.Koza1PointCrossover(maxdepth,cp_my_tree1,cp_my_tree2,cp_my_tree1_mapping,cp_my_tree2_mapping,my_tree1depth,my_tree2depth)
337
338
339 i=i+1
340
341
342
343
344
345 if cs[0]!=[1,1,1,1] and settings.Substitute_Mutation==1:
346 mt=mutation.Mutate(maxdepth,cp_my_tree1,cp_my_tree1_mapping,my_tree1depth)
347 mt_map=crossutil.GetIndicesMappingFromTree(mt[1])
348 mt_depth=crossutil.GetDepthFromIndicesMapping(mt_map)
349 mt_evaluated=1
350
351 result_fitness=settings.FitnessFunction(mt[1])
352 new_pop.append((o_id,mt[1], mt_map,mt_depth,mt_evaluated,result_fitness))
353 else:
354
355 try:
356 offspring1_result_fitness=settings.FitnessFunction(cs[1])
357
358
359
360 except:
361 print 'pb when applying fitness function to results of crossover'
362 print cs[1]
363
364 print cs[0]
365 try:
366
367 offspring2_result_fitness=settings.FitnessFunction(cs[2])
368
369
370 except:
371 print 'pb when applying fitness function to results of crossover'
372
373 print cs[2]
374 print cs[0]
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391 if offspring1_result_fitness>=offspring2_result_fitness:
392 cs_map=crossutil.GetIndicesMappingFromTree(cs[1])
393 cs_depth=crossutil.GetDepthFromIndicesMapping(cs_map)
394
395 new_pop.append((o_id,cs[1], cs_map,cs_depth,cs_evaluated,offspring1_result_fitness))
396
397 if offspring1_result_fitness<offspring2_result_fitness:
398 cs_map=crossutil.GetIndicesMappingFromTree(cs[2])
399 cs_depth=crossutil.GetDepthFromIndicesMapping(cs_map)
400
401 new_pop.append((o_id,cs[2], cs_map,cs_depth,cs_evaluated,offspring2_result_fitness))
402
403 con.commit()
404 con.close()
405
406 con = sqlite.connect(dbname)
407
408 con.execute("create table %s(o_id INTEGER PRIMARY KEY,tree TEXT,\
409 tree_mapping TEXT, treedepth INTEGER, evaluated INTEGER, fitness FLOAT)"%tablename2)
410
411 for i in xrange(0,popsize):
412 con.execute("insert into %s(o_id,tree,tree_mapping,treedepth,evaluated,fitness) values (NULL,?,?,?,?,?)"%tablename2,(buffer(marshal.dumps(new_pop[i][1],-1)),buffer(marshal.dumps(new_pop[i][2],-1)),new_pop[i][3],new_pop[i][4],new_pop[i][5]))
413 con.commit()
414 con.close()
415
416
417
418
419
420
421 if __name__ == '__main__':
422
423 dbname=r'D:\mehdi\python projects\pySTEP_0.95\src\pop_db'
424
425
426
427
428
429
430
431
432
433
434 EvolutionRun(2000,(0,3,'root'),2,6,'AddHalfNode',100, 0.1 ,0.5,0.49,7,0.8,dbname,True)
435