ga/src/gas/parallelga.cpp

00001 /********************************************************************************
00002  *  FARSA Genetic Algorithm Library                                             *
00003  *  Copyright (C) 2007-2009 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #include "factory.h"
00021 #include "gas/parallelga.h"
00022 #include "core/reproduction.h"
00023 #include "core/genome.h"
00024 #include "core/evaluation.h"
00025 #include <QThreadPool>
00026 #include <QtConcurrentMap>
00027 using namespace QtConcurrent;
00028 
00029 namespace farsa {
00030 
00031 ParallelGA::ParallelGA()
00032     : GeneticAlgo() {
00033     fitfunc = 0;
00034     reprod = 0;
00035     numGens = 0;
00036     currPhase = initEvaluation;
00037     numThreadv = 1;
00038     isInitialized = false;
00039     isFinalized = true;
00040     future = new QFuture<void>();
00041 }
00042 
00043 ParallelGA::~ParallelGA() {
00044     foreach( ParallelGA::evaluationThread* e, evalThreads ) {
00045         delete e;
00046     }
00047     delete fitfunc;
00048 }
00049 
00050 void ParallelGA::configure( ConfigurationParameters& params, QString prefix ) {
00051     setGenome( params.getObjectFromGroup<Genome>( prefix + QString( "GENOME" ) ) );
00052     setEvaluation( params.getObjectFromGroup<Evaluation>( prefix + QString( "EVALUATION" ) ) );
00053     fitfunc->setGenome( genome() );
00054     setReproduction( params.getObjectFromGroup<Reproduction>( prefix + QString( "REPRODUCTION" ) ) );
00055     setNumGenerations( params.getValue( prefix + QString( "ngenerations" ) ).toInt() );
00056     if ( numGenerations() == 0 ) {
00057         qWarning( "Setting number of Generations to ZERO!! Check your config file" );
00058     }
00059     setNumThreads( params.getValue( prefix + QString( "numThreads" ) ).toInt() );
00060 }
00061 
00062 void ParallelGA::save( ConfigurationParameters& params, QString prefix ) {
00063     params.createParameter( prefix, QString("type"), "ParallelGA" );
00064     params.createParameter( prefix, QString("numThreads"), QString("%1").arg( numThreads() ) );
00065     params.createParameter( prefix, QString("ngenerations"), QString("%1").arg( numGenerations() ) );
00066     //--- EVALUATION
00067     fitfunc->save( params, params.createSubGroup( prefix, "EVALUATION" ) );
00068     //--- REPRODUCTION
00069     reproduction()->save( params, params.createSubGroup( prefix, "REPRODUCTION" ) );
00070     //--- GENOME
00071     genome()->save( params, params.createSubGroup( prefix, "GENOME" ) );
00072 }
00073 
00074 void ParallelGA::describe( QString type ) {
00075     Descriptor d = addTypeDescription( type, "Parallel Genetic Algorithm", "Respect to SimpleGA and others type of Genetic Algorithm, the implementation of the parallelization is more efficient" );
00076     d.describeInt( "numThreads" ).limits( 1, 32 ).help( "Number of threads to parallelize the evaluation of individuals" );
00077     d.describeInt( "ngenerations" ).limits( 1, INT_MAX ).def( 1000 ).help( "Number of the generations of the evolutionary process" );
00078     d.describeSubgroup( "EVALUATION" ).type( "Evaluation" ).props( IsMandatory ).help( "Object that calculate the fitness", "Create a subclass of Evalution and code your custom fitness function" );
00079     d.describeSubgroup( "REPRODUCTION").type( "Reproduction" ).props( IsMandatory ).help( "Object that generate the new generations" );
00080     d.describeSubgroup( "GENOME" ).type( "Genome" ).props( IsMandatory ).help( "Object containing the individuals under evolution" );
00081 }
00082 
00083 void ParallelGA::setNumThreads( int numThreads ) {
00084     if ( numThreads < 1 ) {
00085         qWarning( "The number of Threads must be greater than one!!" );
00086     }
00087     Q_ASSERT_X( !isInitialized && isFinalized ,
00088             "ParallelGA::setNumThreads",
00089             "This method can only called before initialize of ParallelGA" );
00090     Q_ASSERT_X( fitfunc != 0 ,
00091             "ParallelGA::setNumThreads",
00092             "This method must be called after an Evaluation object has been setted by ParallelGA::setEvaluation" );
00093     numThreadv = numThreads;
00094     return;
00095 }
00096 
00097 int ParallelGA::numThreads() {
00098     return numThreadv;
00099 }
00100 
00101 void ParallelGA::setEvaluation( Evaluation* fitfunc ) {
00102     this->fitfunc = fitfunc;
00103     this->fitfunc->setGA( this );
00104 }
00105 
00106 Evaluation* ParallelGA::evaluationPrototype()
00107 {
00108     return fitfunc;
00109 }
00110 
00111 QVector<Evaluation*> ParallelGA::evaluationPool() {
00112     QVector<Evaluation*> ev;
00113     foreach( ParallelGA::evaluationThread* e, evalThreads ) {
00114         ev.append( e->eval );
00115     }
00116     return ev;
00117 }
00118 
00119 void ParallelGA::setReproduction( Reproduction* reprod ) {
00120     this->reprod = reprod;
00121     this->reprod->setGA( this );
00122 }
00123 
00124 Reproduction* ParallelGA::reproduction() {
00125     return reprod;
00126 }
00127 
00128 void ParallelGA::initialize() {
00129     if ( isInitialized && !isFinalized ) return;
00130     Q_ASSERT_X( fitfunc != 0 ,
00131             "ParallelGA::initialize",
00132             "You have to setup the Evaluation object of ParallelGA (Fitness Function)" );
00133     Q_ASSERT_X( reprod !=0 ,
00134             "ParallelGA::initialize",
00135             "You have to setup the Reproduction operator of ParallelGA" );
00136 
00137     isInitialized = true;
00138     isFinalized = false;
00139     setGeneration( 0 );
00140     currPhase = initEvaluation;
00141     evolutionEnd = false;
00142     evaluationDone = false;
00143     //--- Setting up the evalThreads
00144     for( int i=0; i<evalThreads.size(); i++ ) {
00145         delete (evalThreads[i]);
00146     }
00147     evalThreads.clear();
00148     for( int i=0; i<numThreadv; i++ ) {
00149         evalThreads.append( new evaluationThread( fitfunc ) );
00150     }
00151     //--- set the number of thread to create
00152     QThreadPool::globalInstance()->setMaxThreadCount( numThreadv );
00153 }
00154 
00155 void ParallelGA::gaStep() {
00156     switch( currPhase ) {
00157     case initEvaluation:
00158         for( int i=0; i<numThreadv; i++ ) {
00159             evalThreads[i]->sequence.clear();
00160         }
00161         for( int i=0; i<(int)genome()->size(); i++ ) {
00162             evalThreads[ i%numThreadv ]->sequence.append( i );
00163         }
00164         for( int i=0; i<numThreadv; i++ ) {
00165             evalThreads[i]->idSeq = 0;
00166             evalThreads[i]->id = evalThreads[i]->sequence[ evalThreads[i]->idSeq ];
00167             evalThreads[i]->eval->setGenome( genome() );
00168         }
00169         currPhase = evaluating;
00170         //--- here also starts to run all sub-threads for evaluation
00171         //--- it evaluate all genotypes of the population in separate threads
00172         (*future) = map( evalThreads, ParallelGA::runStepWrapper );
00173         break;
00174     case evaluating: /* Multi Thread Block (i.e. Parallel Evaluation of Genotypes */
00175         //--- check if the evaluation has been completed
00176         if ( future->isFinished() ) {
00177             currPhase = nextGeneration_pass1;
00178         }
00179         break;
00180     case nextGeneration_pass1:
00181         qSort( genome()->begin(), genome()->end(), Genotype::rankGreaterThanComparator );
00182         updateStats();
00183         evaluationDone = true;
00184         if ( generation() < numGens ) {
00185             currPhase = nextGeneration_pass2;
00186         } else {
00187             currPhase = endEvolution;
00188         }
00189         break;
00190     case nextGeneration_pass2: {
00191         //--- this additional pass is for avoid to modify the
00192         //--- genotypes contained in the last generation of the evolution
00193         //--- and to allow to save the genotypes after each generation
00194         Genome* old = genome();
00195         setGenome( reprod->reproduction( old ) );
00196         delete old;
00197         fitfunc->setGenome( genome() );
00198         evaluationDone = false;
00199         setGeneration( generation()+1 );
00200         currPhase = initEvaluation;
00201         }
00202         break;
00203     case endEvolution:
00204         finalize();
00205         break;
00206     default:
00207         qFatal( "Default switch in ParallelGA::gaStep" );
00208         break;
00209     }
00210 }
00211 
00212 void ParallelGA::skipEvaluation() {
00213     // Set evaluation done, and check which phase to go to
00214     evaluationDone = true;
00215     if ( generation() < numGens ) {
00216         currPhase = nextGeneration_pass2;
00217     } else {
00218         currPhase = endEvolution;
00219     }
00220 }
00221 
00222 void ParallelGA::finalize() {
00223     if ( isFinalized && !isInitialized ) return;
00224 
00225     isInitialized = false;
00226     isFinalized = true;
00227     evolutionEnd = true;
00228 }
00229 
00230 ParallelGA::evaluationThread::evaluationThread( Evaluation* eProto )
00231     : id(0) {
00232     eval = eProto->clone();
00233     eval->setGenome( eProto->GA()->genome() );
00234     eval->setGA( eProto->GA() );
00235 }
00236 
00237 ParallelGA::evaluationThread::~evaluationThread() {
00238     delete eval;
00239     sequence.clear();
00240 }
00241 
00242 void ParallelGA::evaluationThread::runStep() {
00243     //--- it evaluate all individual assigned to this thread
00244     while( true ) {
00245         eval->initialize( eval->GA()->genome()->at( id ) );
00246         eval->evaluate();
00247         int nextIdSeq = idSeq + 1;
00248         eval->finalize();
00249         eval->genotype()->setRank( eval->genotype()->fitness() );
00250         if ( nextIdSeq >= sequence.size() ) {
00251             return;
00252         }
00253         idSeq = nextIdSeq;
00254         int nextId = sequence[ idSeq ];
00255         id = nextId;
00256     }
00257 }
00258 
00259 void ParallelGA::runStepWrapper( ParallelGA::evaluationThread* e ) {
00260     e->runStep();
00261 }
00262 
00263 } // end namespace farsa