parallelga.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/parallelga.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 ParallelGA::ParallelGA()
33  : GeneticAlgo() {
34  fitfunc = 0;
35  reprod = 0;
36  numGens = 0;
37  currPhase = initEvaluation;
38  numThreadv = 1;
39  isInitialized = false;
40  isFinalized = true;
41  future = new QFuture<void>();
42 }
43 
45  foreach( ParallelGA::evaluationThread* e, evalThreads ) {
46  delete e;
47  }
48  delete fitfunc;
49 }
50 
51 void ParallelGA::configure( ConfigurationParameters& params, QString prefix ) {
52  setGenome( params.getObjectFromGroup<Genome>( prefix + QString( "GENOME" ) ) );
53  setEvaluation( params.getObjectFromGroup<Evaluation>( prefix + QString( "EVALUATION" ) ) );
54  fitfunc->setGenome( genome() );
55  setReproduction( params.getObjectFromGroup<Reproduction>( prefix + QString( "REPRODUCTION" ) ) );
56  setNumGenerations( ConfigurationHelper::getInt( params, prefix + QString( "ngenerations" ), 1000 ) );
57  setNumThreads( ConfigurationHelper::getInt( params, prefix + QString( "numThreads" ), 1 ) );
58 }
59 
60 void ParallelGA::save( ConfigurationParameters& params, QString prefix ) {
61  params.createParameter( prefix, QString("type"), "ParallelGA" );
62  params.createParameter( prefix, QString("numThreads"), QString("%1").arg( numThreads() ) );
63  params.createParameter( prefix, QString("ngenerations"), QString("%1").arg( numGenerations() ) );
64  //--- EVALUATION
65  fitfunc->save( params, params.createSubGroup( prefix, "EVALUATION" ) );
66  //--- REPRODUCTION
67  reproduction()->save( params, params.createSubGroup( prefix, "REPRODUCTION" ) );
68  //--- GENOME
69  genome()->save( params, params.createSubGroup( prefix, "GENOME" ) );
70 }
71 
72 void ParallelGA::describe( QString type ) {
73  Descriptor d = addTypeDescription( type, "Parallel Genetic Algorithm", "Respect to SimpleGA and others type of Genetic Algorithm, the implementation of the parallelization is more efficient" );
74  d.describeInt( "numThreads" ).limits( 1, 32 ).def(1).help( "Number of threads to parallelize the evaluation of individuals" );
75  d.describeInt( "ngenerations" ).limits( 1, INT_MAX ).def( 1000 ).help( "Number of the generations of the evolutionary process" );
76  d.describeSubgroup( "EVALUATION" ).type( "Evaluation" ).props( IsMandatory ).help( "Object that calculate the fitness", "Create a subclass of Evalution and code your custom fitness function" );
77  d.describeSubgroup( "REPRODUCTION").type( "Reproduction" ).props( IsMandatory ).help( "Object that generate the new generations" );
78  d.describeSubgroup( "GENOME" ).type( "Genome" ).props( IsMandatory ).help( "Object containing the individuals under evolution" );
79 }
80 
81 void ParallelGA::setNumThreads( int numThreads ) {
82  if ( numThreads < 1 ) {
83  qWarning( "The number of Threads must be greater than one!!" );
84  }
85  Q_ASSERT_X( !isInitialized && isFinalized ,
86  "ParallelGA::setNumThreads",
87  "This method can only called before initialize of ParallelGA" );
88  Q_ASSERT_X( fitfunc != 0 ,
89  "ParallelGA::setNumThreads",
90  "This method must be called after an Evaluation object has been setted by ParallelGA::setEvaluation" );
91  numThreadv = numThreads;
92  return;
93 }
94 
96  return numThreadv;
97 }
98 
100  this->fitfunc = fitfunc;
101  this->fitfunc->setGA( this );
102 }
103 
105 {
106  return fitfunc;
107 }
108 
109 QVector<Evaluation*> ParallelGA::evaluationPool() {
110  QVector<Evaluation*> ev;
111  foreach( ParallelGA::evaluationThread* e, evalThreads ) {
112  ev.append( e->eval );
113  }
114  return ev;
115 }
116 
118  this->reprod = reprod;
119  this->reprod->setGA( this );
120 }
121 
123  return reprod;
124 }
125 
127  if ( isInitialized && !isFinalized ) return;
128  Q_ASSERT_X( fitfunc != 0 ,
129  "ParallelGA::initialize",
130  "You have to setup the Evaluation object of ParallelGA (Fitness Function)" );
131  Q_ASSERT_X( reprod !=0 ,
132  "ParallelGA::initialize",
133  "You have to setup the Reproduction operator of ParallelGA" );
134 
135  isInitialized = true;
136  isFinalized = false;
137  setGeneration( 0 );
138  currPhase = initEvaluation;
139  evolutionEnd = false;
140  evaluationDone = false;
141  //--- Setting up the evalThreads
142  for( int i=0; i<evalThreads.size(); i++ ) {
143  delete (evalThreads[i]);
144  }
145  evalThreads.clear();
146  for( int i=0; i<numThreadv; i++ ) {
147  evalThreads.append( new evaluationThread( fitfunc ) );
148  evalThreads.last()->eval->initGeneration(0);
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  }
168  currPhase = evaluating;
169  //--- here also starts to run all sub-threads for evaluation
170  //--- it evaluate all genotypes of the population in separate threads
171  (*future) = map( evalThreads, ParallelGA::runStepWrapper );
172  break;
173  case evaluating: /* Multi Thread Block (i.e. Parallel Evaluation of Genotypes */
174  //--- check if the evaluation has been completed
175  if ( future->isFinished() ) {
176  currPhase = nextGeneration_pass1;
177  }
178  break;
179  case nextGeneration_pass1:
180  qSort( genome()->begin(), genome()->end(), Genotype::rankGreaterThanComparator );
181  updateStats();
182  evaluationDone = true;
183  for( int i=0; i<numThreadv; i++ ) {
184  evalThreads[i]->eval->endGeneration( generation() );
185  }
186  if ( generation() < numGens ) {
187  currPhase = nextGeneration_pass2;
188  } else {
189  currPhase = endEvolution;
190  }
191  break;
192  case nextGeneration_pass2: {
193  //--- this additional pass is for avoid to modify the
194  //--- genotypes contained in the last generation of the evolution
195  //--- and to allow to save the genotypes after each generation
196  Genome* old = genome();
197  setGenome( reprod->reproduction( old ) );
198  delete old;
199  fitfunc->setGenome( genome() );
200  evaluationDone = false;
201  setGeneration( generation()+1 );
202  for( int i=0; i<numThreadv; i++ ) {
203  evalThreads[i]->eval->initGeneration( generation() );
204  }
205  currPhase = initEvaluation;
206  }
207  break;
208  case endEvolution:
209  finalize();
210  break;
211  default:
212  qFatal( "Default switch in ParallelGA::gaStep" );
213  break;
214  }
215 }
216 
218  // Set evaluation done, and check which phase to go to
219  evaluationDone = true;
220  if ( generation() < numGens ) {
221  currPhase = nextGeneration_pass2;
222  } else {
223  currPhase = endEvolution;
224  }
225 }
226 
228  if ( isFinalized && !isInitialized ) return;
229 
230  isInitialized = false;
231  isFinalized = true;
232  evolutionEnd = true;
233 }
234 
235 ParallelGA::evaluationThread::evaluationThread( Evaluation* eProto )
236  : id(0) {
237  eval = eProto->clone();
238  eval->setGenome( eProto->GA()->genome() );
239  eval->setGA( eProto->GA() );
240 }
241 
242 ParallelGA::evaluationThread::~evaluationThread() {
243  delete eval;
244  sequence.clear();
245 }
246 
247 void ParallelGA::evaluationThread::runStep() {
248  //--- it evaluate all individual assigned to this thread
249  while( true ) {
250  eval->initialize( eval->GA()->genome()->at( id ) );
251  eval->evaluate();
252  int nextIdSeq = idSeq + 1;
253  eval->finalize();
254  eval->genotype()->setRank( eval->genotype()->fitness() );
255  if ( nextIdSeq >= sequence.size() ) {
256  return;
257  }
258  idSeq = nextIdSeq;
259  int nextId = sequence[ idSeq ];
260  id = nextId;
261  }
262 }
263 
264 void ParallelGA::runStepWrapper( ParallelGA::evaluationThread* e ) {
265  e->runStep();
266 }
267 
268 } // end namespace farsa