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 <QThreadPool>
26 #include <QtConcurrentMap>
27 using namespace QtConcurrent;
28 
29 namespace farsa {
30 
31 SimpleGA::SimpleGA()
32  : GeneticAlgo() {
33  fitfunc = 0;
34  reprod = 0;
35  numGens = 0;
36  currPhase = initEvaluation;
37  numThreadv = 1;
38  isInitialized = false;
39  isFinalized = true;
40 }
41 
43  foreach( SimpleGA::evaluationThread* e, evalThreads ) {
44  delete e;
45  }
46  delete fitfunc;
47 }
48 
49 void SimpleGA::configure( ConfigurationParameters& params, QString prefix ) {
50  setGenome( params.getObjectFromGroup<Genome>( prefix + QString( "GENOME" ) ) );
51  setEvaluation( params.getObjectFromGroup<Evaluation>( prefix + QString( "EVALUATION" ) ) );
52  fitfunc->setGenome( genome() );
53  setReproduction( params.getObjectFromGroup<Reproduction>( prefix + QString( "REPRODUCTION" ) ) );
54  setNumGenerations( params.getValue( prefix + QString( "ngenerations" ) ).toInt() );
55  if ( numGenerations() == 0 ) {
56  qWarning( "Setting number of Generations to ZERO!! Check your config file" );
57  }
58  setNumThreads( params.getValue( prefix + QString( "numThreads" ) ).toInt() );
59 }
60 
61 void SimpleGA::save( ConfigurationParameters& params, QString prefix ) {
62  params.createParameter( prefix, QString("type"), "SimpleGA" );
63  params.createParameter( prefix, QString("numThreads"), QString("%1").arg( numThreads() ) );
64  params.createParameter( prefix, QString("ngenerations"), QString("%1").arg( numGenerations() ) );
65  //--- EVALUATION
66  fitfunc->save( params, params.createSubGroup( prefix, "EVALUATION" ) );
67  //--- REPRODUCTION
68  reproduction()->save( params, params.createSubGroup( prefix, "REPRODUCTION" ) );
69  //--- GENOME
70  genome()->save( params, params.createSubGroup( prefix, "GENOME" ) );
71 }
72 
73 void SimpleGA::describe( QString type ) {
74  Descriptor d = addTypeDescription( type, "Simple Genetic Algorithm" );
75  d.describeInt( "numThreads" ).limits( 1, 32 ).help( "Number of threads to parallelize the evaluation of individuals" );
76  d.describeInt( "ngenerations" ).limits( 1, INT_MAX ).def( 1000 ).help( "Number of the generations of the evolutionary process" );
77  d.describeSubgroup( "EVALUATION" ).type( "Evaluation" ).props( IsMandatory ).help( "Object that calculate the fitness", "Create a subclass of Evalution and code your custom fitness function" );
78  d.describeSubgroup( "REPRODUCTION").type( "Reproduction" ).props( IsMandatory ).help( "Object that generate the new generations" );
79  d.describeSubgroup( "GENOME" ).type( "Genome" ).props( IsMandatory ).help( "Object containing the individuals under evolution" );
80 }
81 
82 void SimpleGA::setNumThreads( int numThreads ) {
83  if ( numThreads < 1 ) {
84  qWarning( "The number of Threads must be greater than one!!" );
85  }
86  Q_ASSERT_X( !isInitialized && isFinalized ,
87  "SimpleGA::setNumThreads",
88  "This method can only called before initialize of SimpleGA" );
89  Q_ASSERT_X( fitfunc != 0 ,
90  "SimpleGA::setNumThreads",
91  "This method must be called after an Evaluation object has been setted by SimpleGA::setEvaluation" );
92  numThreadv = numThreads;
93  return;
94 }
95 
97  return numThreadv;
98 }
99 
101  this->fitfunc = fitfunc;
102  this->fitfunc->setGA( this );
103 }
104 
106 {
107  return fitfunc;
108 }
109 
110 QVector<Evaluation*> SimpleGA::evaluationPool() {
111  QVector<Evaluation*> ev;
112  foreach( SimpleGA::evaluationThread* e, evalThreads ) {
113  ev.append( e->eval );
114  }
115  return ev;
116 }
117 
119  this->reprod = reprod;
120  this->reprod->setGA( this );
121 }
122 
124  return reprod;
125 }
126 
128  if ( isInitialized && !isFinalized ) return;
129  Q_ASSERT_X( fitfunc != 0 ,
130  "SimpleGA::initialize",
131  "You have to setup the Evaluation object of SimpleGA (Fitness Function)" );
132  Q_ASSERT_X( reprod !=0 ,
133  "SimpleGA::initialize",
134  "You have to setup the Reproduction operator of SimpleGA" );
135 
136  isInitialized = true;
137  isFinalized = false;
138  setGeneration( 0 );
139  currPhase = initEvaluation;
140  evolutionEnd = false;
141  evaluationDone = false;
142  //--- Setting up the evalThreads
143  for( int i=0; i<evalThreads.size(); i++ ) {
144  delete (evalThreads[i]);
145  }
146  evalThreads.clear();
147  for( int i=0; i<numThreadv; i++ ) {
148  evalThreads.append( new evaluationThread( this, fitfunc ) );
149  }
150  //--- set the number of thread to create
151  QThreadPool::globalInstance()->setMaxThreadCount( numThreadv );
152 }
153 
155  switch( currPhase ) {
156  case initEvaluation:
157  for( int i=0; i<numThreadv; i++ ) {
158  evalThreads[i]->sequence.clear();
159  }
160  for( int i=0; i<(int)genome()->size(); i++ ) {
161  evalThreads[ i%numThreadv ]->sequence.append( i );
162  }
163  for( int i=0; i<numThreadv; i++ ) {
164  evalThreads[i]->idSeq = 0;
165  evalThreads[i]->id = evalThreads[i]->sequence[ evalThreads[i]->idSeq ];
166  evalThreads[i]->eval->setGenome( genome() );
167  evalThreads[i]->eval->initialize( genome()->at( evalThreads[i]->id ) );
168  evalThreads[i]->blocked = false;
169  }
170  currPhase = evaluating;
171  break;
172  case evaluating: { /* Multi Thread Block (i.e. Parallel Evaluation of Genotypes */
173  nextGeneration = true;
174  if ( numThreadv == 1 ) {
175  // Don't use Threads if is not necessary
176  evalThreads[0]->runStep();
177  } else {
178  QFuture<void> future = map( evalThreads, SimpleGA::runStepWrapper );
179  future.waitForFinished();
180  }
181  if ( nextGeneration ) {
182  currPhase = nextGeneration_pass1;
183  }
184  } /* End of Multi Thread Block */
185  break;
186  case nextGeneration_pass1:
187  qSort( genome()->begin(), genome()->end(), Genotype::rankGreaterThanComparator );
188  updateStats();
189  evaluationDone = true;
190  if ( generation() < numGens ) {
191  currPhase = nextGeneration_pass2;
192  } else {
193  currPhase = endEvolution;
194  }
195  break;
196  case nextGeneration_pass2: {
197  //--- this additional pass is for avoid to modify the
198  //--- genotypes contained in the last generation of the evolution
199  //--- and to allow to save the genotypes after each generation
200  Genome* old = genome();
201  setGenome( reprod->reproduction( old ) );
202  delete old;
203  fitfunc->setGenome( genome() );
204  evaluationDone = false;
205  setGeneration( generation()+1 );
206  currPhase = initEvaluation;
207  }
208  break;
209  case endEvolution:
210  finalize();
211  break;
212  default:
213  qFatal( "Default switch in SimpleGA::gaStep" );
214  break;
215  }
216 }
217 
219  // Set evaluation done, and check which phase to go to
220  evaluationDone = true;
221  if ( generation() < numGens ) {
222  currPhase = nextGeneration_pass2;
223  } else {
224  currPhase = endEvolution;
225  }
226 }
227 
229  if ( isFinalized && !isInitialized ) return;
230 
231  isInitialized = false;
232  isFinalized = true;
233  evolutionEnd = true;
234 }
235 
236 SimpleGA::evaluationThread::evaluationThread( SimpleGA* p, Evaluation* eProto )
237  : parent(p), id(0), blocked(false) {
238  eval = eProto->clone();
239  eval->setGenome( p->genome() );
240  eval->setGA( p );
241 }
242 
243 SimpleGA::evaluationThread::~evaluationThread() {
244  delete eval;
245  sequence.clear();
246 }
247 
248 void SimpleGA::evaluationThread::runStep() {
249  if ( blocked ) {
250  return;
251  }
252 
253  parent->nextGeneration = false;
254  eval->evaluateStep();
255  if ( eval->isEvaluationDone() ) {
256  int nextIdSeq = idSeq + 1;
257  eval->finalize();
258  eval->genotype()->setRank( eval->genotype()->fitness() );
259  if ( nextIdSeq >= sequence.size() ) {
260  blocked = true;
261  return;
262  }
263  idSeq = nextIdSeq;
264  int nextId = sequence[ idSeq ];
265  id = nextId;
266  eval->initialize( parent->genome()->at( id ) );
267  }
268 }
269 
270 void SimpleGA::runStepWrapper( SimpleGA::evaluationThread* e ) {
271  e->runStep();
272 }
273 
274 } // end namespace farsa