simplega.cpp
1 /********************************************************************************
2  * FARSA Genetic Algorithm Library *
3  * Copyright (C) 2007-2009 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "factory.h"
21 #include "gas/simplega.h"
22 #include "core/reproduction.h"
23 #include "core/genome.h"
24 #include "core/evaluation.h"
25 #include "configurationhelper.h"
26 #include <QThreadPool>
27 #include <QtConcurrentMap>
28 using namespace QtConcurrent;
29 
30 namespace farsa {
31 
32 SimpleGA::SimpleGA()
33  : GeneticAlgo() {
34  fitfunc = 0;
35  reprod = 0;
36  numGens = 0;
37  currPhase = initEvaluation;
38  numThreadv = 1;
39  isInitialized = false;
40  isFinalized = true;
41 }
42 
44  foreach( SimpleGA::evaluationThread* e, evalThreads ) {
45  delete e;
46  }
47  delete fitfunc;
48 }
49 
50 void SimpleGA::configure( ConfigurationParameters& params, QString prefix ) {
51  setGenome( params.getObjectFromGroup<Genome>( prefix + QString( "GENOME" ) ) );
52  setEvaluation( params.getObjectFromGroup<Evaluation>( prefix + QString( "EVALUATION" ) ) );
53  fitfunc->setGenome( genome() );
54  setReproduction( params.getObjectFromGroup<Reproduction>( prefix + QString( "REPRODUCTION" ) ) );
55  setNumGenerations( ConfigurationHelper::getInt( params, prefix + QString( "ngenerations" ), 1000 ) );
56  setNumThreads( ConfigurationHelper::getInt( params, prefix + QString( "numThreads" ), 1 ) );
57 }
58 
59 void SimpleGA::save( ConfigurationParameters& params, QString prefix ) {
60  params.createParameter( prefix, QString("type"), "SimpleGA" );
61  params.createParameter( prefix, QString("numThreads"), QString("%1").arg( numThreads() ) );
62  params.createParameter( prefix, QString("ngenerations"), QString("%1").arg( numGenerations() ) );
63  //--- EVALUATION
64  fitfunc->save( params, params.createSubGroup( prefix, "EVALUATION" ) );
65  //--- REPRODUCTION
66  reproduction()->save( params, params.createSubGroup( prefix, "REPRODUCTION" ) );
67  //--- GENOME
68  genome()->save( params, params.createSubGroup( prefix, "GENOME" ) );
69 }
70 
71 void SimpleGA::describe( QString type ) {
72  Descriptor d = addTypeDescription( type, "Simple Genetic Algorithm" );
73  d.describeInt( "numThreads" ).limits( 1, 32 ).def(1).help( "Number of threads to parallelize the evaluation of individuals" );
74  d.describeInt( "ngenerations" ).limits( 1, INT_MAX ).def( 1000 ).help( "Number of the generations of the evolutionary process" );
75  d.describeSubgroup( "EVALUATION" ).type( "Evaluation" ).props( IsMandatory ).help( "Object that calculate the fitness", "Create a subclass of Evalution and code your custom fitness function" );
76  d.describeSubgroup( "REPRODUCTION").type( "Reproduction" ).props( IsMandatory ).help( "Object that generate the new generations" );
77  d.describeSubgroup( "GENOME" ).type( "Genome" ).props( IsMandatory ).help( "Object containing the individuals under evolution" );
78 }
79 
80 void SimpleGA::setNumThreads( int numThreads ) {
81  if ( numThreads < 1 ) {
82  qWarning( "The number of Threads must be greater than one!!" );
83  }
84  Q_ASSERT_X( !isInitialized && isFinalized ,
85  "SimpleGA::setNumThreads",
86  "This method can only called before initialize of SimpleGA" );
87  Q_ASSERT_X( fitfunc != 0 ,
88  "SimpleGA::setNumThreads",
89  "This method must be called after an Evaluation object has been setted by SimpleGA::setEvaluation" );
90  numThreadv = numThreads;
91  return;
92 }
93 
95  return numThreadv;
96 }
97 
99  this->fitfunc = fitfunc;
100  this->fitfunc->setGA( this );
101 }
102 
104 {
105  return fitfunc;
106 }
107 
108 QVector<Evaluation*> SimpleGA::evaluationPool() {
109  QVector<Evaluation*> ev;
110  foreach( SimpleGA::evaluationThread* e, evalThreads ) {
111  ev.append( e->eval );
112  }
113  return ev;
114 }
115 
117  this->reprod = reprod;
118  this->reprod->setGA( this );
119 }
120 
122  return reprod;
123 }
124 
126  if ( isInitialized && !isFinalized ) return;
127  Q_ASSERT_X( fitfunc != 0 ,
128  "SimpleGA::initialize",
129  "You have to setup the Evaluation object of SimpleGA (Fitness Function)" );
130  Q_ASSERT_X( reprod !=0 ,
131  "SimpleGA::initialize",
132  "You have to setup the Reproduction operator of SimpleGA" );
133 
134  isInitialized = true;
135  isFinalized = false;
136  setGeneration( 0 );
137  currPhase = initEvaluation;
138  evolutionEnd = false;
139  evaluationDone = false;
140  //--- Setting up the evalThreads
141  for( int i=0; i<evalThreads.size(); i++ ) {
142  delete (evalThreads[i]);
143  }
144  evalThreads.clear();
145  for( int i=0; i<numThreadv; i++ ) {
146  evalThreads.append( new evaluationThread( this, fitfunc ) );
147  evalThreads.last()->eval->initGeneration( 0 );
148  }
149  //--- set the number of thread to create
150  QThreadPool::globalInstance()->setMaxThreadCount( numThreadv );
151 }
152 
154  switch( currPhase ) {
155  case initEvaluation:
156  for( int i=0; i<numThreadv; i++ ) {
157  evalThreads[i]->sequence.clear();
158  }
159  for( int i=0; i<(int)genome()->size(); i++ ) {
160  evalThreads[ i%numThreadv ]->sequence.append( i );
161  }
162  for( int i=0; i<numThreadv; i++ ) {
163  evalThreads[i]->idSeq = 0;
164  evalThreads[i]->id = evalThreads[i]->sequence[ evalThreads[i]->idSeq ];
165  evalThreads[i]->eval->setGenome( genome() );
166  evalThreads[i]->eval->initialize( genome()->at( evalThreads[i]->id ) );
167  evalThreads[i]->blocked = false;
168  }
169  currPhase = evaluating;
170  break;
171  case evaluating: { /* Multi Thread Block (i.e. Parallel Evaluation of Genotypes */
172  nextGeneration = true;
173  if ( numThreadv == 1 ) {
174  // Don't use Threads if is not necessary
175  evalThreads[0]->runStep();
176  } else {
177  QFuture<void> future = map( evalThreads, SimpleGA::runStepWrapper );
178  future.waitForFinished();
179  }
180  if ( nextGeneration ) {
181  currPhase = nextGeneration_pass1;
182  }
183  } /* End of Multi Thread Block */
184  break;
185  case nextGeneration_pass1:
186  qSort( genome()->begin(), genome()->end(), Genotype::rankGreaterThanComparator );
187  updateStats();
188  evaluationDone = true;
189  for( int i=0; i<numThreadv; i++ ) {
190  evalThreads[i]->eval->endGeneration( generation() );
191  }
192  if ( generation() < numGens ) {
193  currPhase = nextGeneration_pass2;
194  } else {
195  currPhase = endEvolution;
196  }
197  break;
198  case nextGeneration_pass2: {
199  //--- this additional pass is for avoid to modify the
200  //--- genotypes contained in the last generation of the evolution
201  //--- and to allow to save the genotypes after each generation
202  Genome* old = genome();
203  setGenome( reprod->reproduction( old ) );
204  delete old;
205  fitfunc->setGenome( genome() );
206  evaluationDone = false;
207  setGeneration( generation()+1 );
208  for( int i=0; i<numThreadv; i++ ) {
209  evalThreads[i]->eval->initGeneration( generation() );
210  }
211  currPhase = initEvaluation;
212  }
213  break;
214  case endEvolution:
215  finalize();
216  break;
217  default:
218  qFatal( "Default switch in SimpleGA::gaStep" );
219  break;
220  }
221 }
222 
224  // Set evaluation done, and check which phase to go to
225  evaluationDone = true;
226  if ( generation() < numGens ) {
227  currPhase = nextGeneration_pass2;
228  } else {
229  currPhase = endEvolution;
230  }
231 }
232 
234  if ( isFinalized && !isInitialized ) return;
235 
236  isInitialized = false;
237  isFinalized = true;
238  evolutionEnd = true;
239 }
240 
241 SimpleGA::evaluationThread::evaluationThread( SimpleGA* p, Evaluation* eProto )
242  : parent(p), id(0), blocked(false) {
243  eval = eProto->clone();
244  eval->setGenome( p->genome() );
245  eval->setGA( p );
246 }
247 
248 SimpleGA::evaluationThread::~evaluationThread() {
249  delete eval;
250  sequence.clear();
251 }
252 
253 void SimpleGA::evaluationThread::runStep() {
254  if ( blocked ) {
255  return;
256  }
257 
258  parent->nextGeneration = false;
259  eval->evaluateStep();
260  if ( eval->isEvaluationDone() ) {
261  int nextIdSeq = idSeq + 1;
262  eval->finalize();
263  eval->genotype()->setRank( eval->genotype()->fitness() );
264  if ( nextIdSeq >= sequence.size() ) {
265  blocked = true;
266  return;
267  }
268  idSeq = nextIdSeq;
269  int nextId = sequence[ idSeq ];
270  id = nextId;
271  eval->initialize( parent->genome()->at( id ) );
272  }
273 }
274 
275 void SimpleGA::runStepWrapper( SimpleGA::evaluationThread* e ) {
276  e->runStep();
277 }
278 
279 } // end namespace farsa