ga/src/gas/simplega.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/simplega.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 SimpleGA::SimpleGA()
00032     : GeneticAlgo() {
00033     fitfunc = 0;
00034     reprod = 0;
00035     numGens = 0;
00036     currPhase = initEvaluation;
00037     numThreadv = 1;
00038     isInitialized = false;
00039     isFinalized = true;
00040 }
00041 
00042 SimpleGA::~SimpleGA() {
00043     foreach( SimpleGA::evaluationThread* e, evalThreads ) {
00044         delete e;
00045     }
00046     delete fitfunc;
00047 }
00048 
00049 void SimpleGA::configure( ConfigurationParameters& params, QString prefix ) {
00050     setGenome( params.getObjectFromGroup<Genome>( prefix + QString( "GENOME" ) ) );
00051     setEvaluation( params.getObjectFromGroup<Evaluation>( prefix + QString( "EVALUATION" ) ) );
00052     fitfunc->setGenome( genome() );
00053     setReproduction( params.getObjectFromGroup<Reproduction>( prefix + QString( "REPRODUCTION" ) ) );
00054     setNumGenerations( params.getValue( prefix + QString( "ngenerations" ) ).toInt() );
00055     if ( numGenerations() == 0 ) {
00056         qWarning( "Setting number of Generations to ZERO!! Check your config file" );
00057     }
00058     setNumThreads( params.getValue( prefix + QString( "numThreads" ) ).toInt() );
00059 }
00060 
00061 void SimpleGA::save( ConfigurationParameters& params, QString prefix ) {
00062     params.createParameter( prefix, QString("type"), "SimpleGA" );
00063     params.createParameter( prefix, QString("numThreads"), QString("%1").arg( numThreads() ) );
00064     params.createParameter( prefix, QString("ngenerations"), QString("%1").arg( numGenerations() ) );
00065     //--- EVALUATION
00066     fitfunc->save( params, params.createSubGroup( prefix, "EVALUATION" ) );
00067     //--- REPRODUCTION
00068     reproduction()->save( params, params.createSubGroup( prefix, "REPRODUCTION" ) );
00069     //--- GENOME
00070     genome()->save( params, params.createSubGroup( prefix, "GENOME" ) );
00071 }
00072 
00073 void SimpleGA::describe( QString type ) {
00074     Descriptor d = addTypeDescription( type, "Simple Genetic Algorithm" );
00075     d.describeInt( "numThreads" ).limits( 1, 32 ).help( "Number of threads to parallelize the evaluation of individuals" );
00076     d.describeInt( "ngenerations" ).limits( 1, INT_MAX ).def( 1000 ).help( "Number of the generations of the evolutionary process" );
00077     d.describeSubgroup( "EVALUATION" ).type( "Evaluation" ).props( IsMandatory ).help( "Object that calculate the fitness", "Create a subclass of Evalution and code your custom fitness function" );
00078     d.describeSubgroup( "REPRODUCTION").type( "Reproduction" ).props( IsMandatory ).help( "Object that generate the new generations" );
00079     d.describeSubgroup( "GENOME" ).type( "Genome" ).props( IsMandatory ).help( "Object containing the individuals under evolution" );
00080 }
00081 
00082 void SimpleGA::setNumThreads( int numThreads ) {
00083     if ( numThreads < 1 ) {
00084         qWarning( "The number of Threads must be greater than one!!" );
00085     }
00086     Q_ASSERT_X( !isInitialized && isFinalized ,
00087             "SimpleGA::setNumThreads",
00088             "This method can only called before initialize of SimpleGA" );
00089     Q_ASSERT_X( fitfunc != 0 ,
00090             "SimpleGA::setNumThreads",
00091             "This method must be called after an Evaluation object has been setted by SimpleGA::setEvaluation" );
00092     numThreadv = numThreads;
00093     return;
00094 }
00095 
00096 int SimpleGA::numThreads() {
00097     return numThreadv;
00098 }
00099 
00100 void SimpleGA::setEvaluation( Evaluation* fitfunc ) {
00101     this->fitfunc = fitfunc;
00102     this->fitfunc->setGA( this );
00103 }
00104 
00105 Evaluation* SimpleGA::evaluationPrototype()
00106 {
00107     return fitfunc;
00108 }
00109 
00110 QVector<Evaluation*> SimpleGA::evaluationPool() {
00111     QVector<Evaluation*> ev;
00112     foreach( SimpleGA::evaluationThread* e, evalThreads ) {
00113         ev.append( e->eval );
00114     }
00115     return ev;
00116 }
00117 
00118 void SimpleGA::setReproduction( Reproduction* reprod ) {
00119     this->reprod = reprod;
00120     this->reprod->setGA( this );
00121 }
00122 
00123 Reproduction* SimpleGA::reproduction() {
00124     return reprod;
00125 }
00126 
00127 void SimpleGA::initialize() {
00128     if ( isInitialized && !isFinalized ) return;
00129     Q_ASSERT_X( fitfunc != 0 ,
00130             "SimpleGA::initialize",
00131             "You have to setup the Evaluation object of SimpleGA (Fitness Function)" );
00132     Q_ASSERT_X( reprod !=0 ,
00133             "SimpleGA::initialize",
00134             "You have to setup the Reproduction operator of SimpleGA" );
00135 
00136     isInitialized = true;
00137     isFinalized = false;
00138     setGeneration( 0 );
00139     currPhase = initEvaluation;
00140     evolutionEnd = false;
00141     evaluationDone = false;
00142     //--- Setting up the evalThreads
00143     for( int i=0; i<evalThreads.size(); i++ ) {
00144         delete (evalThreads[i]);
00145     }
00146     evalThreads.clear();
00147     for( int i=0; i<numThreadv; i++ ) {
00148         evalThreads.append( new evaluationThread( this, fitfunc ) );
00149     }
00150     //--- set the number of thread to create
00151     QThreadPool::globalInstance()->setMaxThreadCount( numThreadv );
00152 }
00153 
00154 void SimpleGA::gaStep() {
00155     switch( currPhase ) {
00156     case initEvaluation:
00157         for( int i=0; i<numThreadv; i++ ) {
00158             evalThreads[i]->sequence.clear();
00159         }
00160         for( int i=0; i<(int)genome()->size(); i++ ) {
00161             evalThreads[ i%numThreadv ]->sequence.append( i );
00162         }
00163         for( int i=0; i<numThreadv; i++ ) {
00164             evalThreads[i]->idSeq = 0;
00165             evalThreads[i]->id = evalThreads[i]->sequence[ evalThreads[i]->idSeq ];
00166             evalThreads[i]->eval->setGenome( genome() );
00167             evalThreads[i]->eval->initialize( genome()->at( evalThreads[i]->id ) );
00168             evalThreads[i]->blocked = false;
00169         }
00170         currPhase = evaluating;
00171         break;
00172     case evaluating: { /* Multi Thread Block (i.e. Parallel Evaluation of Genotypes */
00173         nextGeneration = true;
00174         if ( numThreadv == 1 ) {
00175             // Don't use Threads if is not necessary
00176             evalThreads[0]->runStep();
00177         } else {
00178             QFuture<void> future = map( evalThreads, SimpleGA::runStepWrapper );
00179             future.waitForFinished();
00180         }
00181         if ( nextGeneration ) {
00182             currPhase = nextGeneration_pass1;
00183         }
00184         } /* End of Multi Thread Block */
00185         break;
00186     case nextGeneration_pass1:
00187         qSort( genome()->begin(), genome()->end(), Genotype::rankGreaterThanComparator );
00188         updateStats();
00189         evaluationDone = true;
00190         if ( generation() < numGens ) {
00191             currPhase = nextGeneration_pass2;
00192         } else {
00193             currPhase = endEvolution;
00194         }
00195         break;
00196     case nextGeneration_pass2: {
00197         //--- this additional pass is for avoid to modify the
00198         //--- genotypes contained in the last generation of the evolution
00199         //--- and to allow to save the genotypes after each generation
00200         Genome* old = genome();
00201         setGenome( reprod->reproduction( old ) );
00202         delete old;
00203         fitfunc->setGenome( genome() );
00204         evaluationDone = false;
00205         setGeneration( generation()+1 );
00206         currPhase = initEvaluation;
00207         }
00208         break;
00209     case endEvolution:
00210         finalize();
00211         break;
00212     default:
00213         qFatal( "Default switch in SimpleGA::gaStep" );
00214         break;
00215     }
00216 }
00217 
00218 void SimpleGA::skipEvaluation() {
00219     // Set evaluation done, and check which phase to go to
00220     evaluationDone = true;
00221     if ( generation() < numGens ) {
00222         currPhase = nextGeneration_pass2;
00223     } else {
00224         currPhase = endEvolution;
00225     }
00226 }
00227 
00228 void SimpleGA::finalize() {
00229     if ( isFinalized && !isInitialized ) return;
00230 
00231     isInitialized = false;
00232     isFinalized = true;
00233     evolutionEnd = true;
00234 }
00235 
00236 SimpleGA::evaluationThread::evaluationThread( SimpleGA* p, Evaluation* eProto )
00237     : parent(p), id(0), blocked(false) {
00238     eval = eProto->clone();
00239     eval->setGenome( p->genome() );
00240     eval->setGA( p );
00241 }
00242 
00243 SimpleGA::evaluationThread::~evaluationThread() {
00244     delete eval;
00245     sequence.clear();
00246 }
00247 
00248 void SimpleGA::evaluationThread::runStep() {
00249     if ( blocked ) {
00250         return;
00251     }
00252     
00253     parent->nextGeneration = false;
00254     eval->evaluateStep();
00255     if ( eval->isEvaluationDone() ) {
00256         int nextIdSeq = idSeq + 1;
00257         eval->finalize();
00258         eval->genotype()->setRank( eval->genotype()->fitness() );
00259         if ( nextIdSeq >= sequence.size() ) {
00260             blocked = true;
00261             return;
00262         }
00263         idSeq = nextIdSeq;
00264         int nextId = sequence[ idSeq ];
00265         id = nextId;
00266         eval->initialize( parent->genome()->at( id ) );
00267     }
00268 }
00269 
00270 void SimpleGA::runStepWrapper( SimpleGA::evaluationThread* e ) {
00271     e->runStep();
00272 }
00273 
00274 } // end namespace farsa