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