stefanosteadystatega.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/stefanosteadystatega.h"
22 #include "core/reproduction.h"
23 #include "core/genome.h"
24 #include "core/evaluation.h"
25 #include "logger.h"
26 #include "configurationhelper.h"
27 #include <QThreadPool>
28 #include <QtConcurrentMap>
29 using namespace QtConcurrent;
30 
31 namespace farsa {
32 
33 StefanoSteadyStateGA::StefanoSteadyStateGA() :
34  GeneticAlgo(),
35  fitfunc(NULL),
36  muta(NULL),
37  curPhase(initParent),
38  curGenotype(0),
39  isInitialized(false),
40  isFinalized(true),
41  numEvaluations(),
42  cumulatedFitness(),
43  offspring(NULL)
44 {
45 }
46 
48  delete fitfunc;
49  delete muta;
50 }
51 
53  setGenome( params.getObjectFromGroup<Genome>( prefix + QString( "GENOME" ) ) );
54  setEvaluation( params.getObjectFromGroup<Evaluation>( prefix + QString( "EVALUATION" ) ) );
55  fitfunc->setGenome( genome() );
56  setMutation( params.getObjectFromGroup<Mutation>( prefix + QString( "MUTATION" ) ) );
57  setNumGenerations( ConfigurationHelper::getInt( params, prefix + QString( "ngenerations" ), 1000 ) );
58 }
59 
60 void StefanoSteadyStateGA::save( ConfigurationParameters& params, QString prefix ) {
61  params.createParameter( prefix, QString("type"), "StefanoSteadyStateGA" );
62  params.createParameter( prefix, QString("ngenerations"), QString("%1").arg( numGenerations() ) );
63  //--- EVALUATION
64  fitfunc->save( params, params.createSubGroup( prefix, "EVALUATION" ) );
65  //--- MUTATION
66  mutation()->save( params, params.createSubGroup( prefix, "MUTATION" ) );
67  //--- GENOME
68  genome()->save( params, params.createSubGroup( prefix, "GENOME" ) );
69 }
70 
71 void StefanoSteadyStateGA::describe( QString type ) {
72  Descriptor d = addTypeDescription( type, "A variant of Steady State Genetic Algorithm developed by Stefano Nolfi" );
73  d.describeInt( "numThreads" ).limits( 1, 32 ).help( "Number of threads to parallelize the evaluation of individuals" );
74  d.describeInt( "ngenerations" ).limits( 1, MaxInteger ).def( 1000 ).help( "Number of the generations of the evolutionary process" );
75  d.describeSubgroup( "EVALUATION" ).type( "Evaluation" ).props( IsMandatory ).help( "Object that calculate the fitness", "Create a subclass of Evalution and code your custom fitness function" );
76  d.describeSubgroup( "MUTATION").type( "Mutation" ).props( IsMandatory ).help( "Object that mutate the genotype of an individual" );
77  d.describeSubgroup( "GENOME" ).type( "Genome" ).props( IsMandatory ).help( "Object containing the individuals under evolution" );
78 }
79 
81  this->fitfunc = fitfunc;
82  this->fitfunc->setGA( this );
83 }
84 
86 {
87  return fitfunc;
88 }
89 
90 QVector<Evaluation*> StefanoSteadyStateGA::evaluationPool() {
91  // For the moment we only have one evaluator (no threads)
92  QVector<Evaluation*> ev;
93  ev.append(fitfunc);
94  return ev;
95 }
96 
98  this->muta = mutation;
99  this->muta->setGA( this );
100 }
101 
103  return muta;
104 }
105 
107  if ( isInitialized && !isFinalized ) return;
108  Q_ASSERT_X( fitfunc != 0 ,
109  "StefanoSteadyStateGA::initialize",
110  "You have to setup the Evaluation object of StefanoSteadyStateGA (Fitness Function)" );
111  Q_ASSERT_X( muta !=0 ,
112  "StefanoSteadyStateGA::initialize",
113  "You have to setup the Mutation operator of StefanoSteadyStateGA" );
114 
115  isInitialized = true;
116  isFinalized = false;
117  setGeneration(0);
118  curPhase = initParent;
119  curGenotype = 0;
120  evolutionEnd = false;
121  evaluationDone = false;
122  delete offspring;
123  offspring = NULL;
124 
125  // Initializing mutation and evaluator
126  muta->setGenome(genome());
129  numEvaluations.fill(0, genome()->size());
130  cumulatedFitness.fill(0.0, genome()->size());
131 }
132 
134  switch (curPhase) {
135  case initParent:
136  {
138  curPhase = evalParent;
139  }
140  break;
141  case evalParent:
142  {
144  if (fitfunc->isEvaluationDone()) {
145  fitfunc->finalize();
149  curPhase = initOffspring;
150  }
151  }
152  break;
153  case initOffspring:
154  {
155  // Generating one offspring of the current genome
156  delete offspring;
160  curPhase = evalOffspring;
161  }
162  break;
163  case evalOffspring:
164  {
166  if (fitfunc->isEvaluationDone()) {
167  fitfunc->finalize();
169  curPhase = compareOffspring;
170  }
171  }
172  break;
173  case compareOffspring:
174  {
175  // Replacing the individual with the lowest fitness with the offspring. First searching
176  // the worst genome...
177  unsigned int worstGenome = 0;
178  double worstGenomeFitness = genome()->at(0)->rank();
179  for (unsigned int i = 1; i < genome()->size(); i++) {
180  if (numEvaluations[i] == 0) {
181  continue;
182  }
183  if (genome()->at(i)->rank() < worstGenomeFitness) {
184  worstGenome = i;
185  worstGenomeFitness = genome()->at(i)->rank();
186  }
187  }
188  // ... now checking if the worst genome is worse than the offspring. If so, swapping
189  if (worstGenomeFitness < offspring->rank()) {
190  *(genome()->at(worstGenome)) = *offspring;
191  cumulatedFitness[worstGenome] = offspring->rank();
192  numEvaluations[worstGenome] = 1;
193  }
194  if (curGenotype < (genome()->size() - 1)) {
195  curGenotype++;
196  curPhase = initParent;
197  } else {
198  evaluationDone = true;
199  curPhase = nextGeneration;
200  }
201  }
202  break;
203  case nextGeneration:
204  {
205  // We don't sort the genome, there is no need to
206  updateStats();
207  evaluationDone = false;
209  if (generation() < numGens) {
210  curPhase = initParent;
211  setGeneration( generation()+1 );
213  curGenotype = 0;
214  } else {
215  curPhase = endEvolution;
216  }
217  }
218  break;
219  case endEvolution:
220  {
221  finalize();
222  }
223  break;
224  default:
225  qFatal("Default switch in StefanoSteadyStateGA::gaStep");
226  break;
227  }
228 }
229 
231  // Set evaluation done, and set the new phase
232  evaluationDone = true;
233  curPhase = nextGeneration;
234 }
235 
237  if ( isFinalized && !isInitialized ) return;
238 
239  isInitialized = false;
240  isFinalized = true;
241  evolutionEnd = true;
242  delete offspring;
243  offspring = NULL;
244 
245  // Rank is already normalized
246 
247  // Finally sorting genome
248  qSort( genome()->begin(), genome()->end(), Genotype::rankGreaterThanComparator );
249 }
250 
251 } // end namespace farsa