faif
EvolutionaryAlgorithm.hpp
1 #ifndef FAIF_SEARCH_EA_H
2 #define FAIF_SEARCH_EA_H
3 
4 //
5 //file with Evolutionary Algorithm implementation
6 //
7 
8 #include <vector>
9 #include <iterator>
10 #include <algorithm>
11 #include <numeric>
12 #include <functional>
13 #include <boost/bind.hpp>
14 #include <boost/concept_check.hpp>
15 
16 #include "../utils/Random.hpp"
17 #include "Space.hpp"
18 
19 namespace faif {
20  namespace search {
21 
22  /** \brief the typedef-s for space for evolutionary algorithm, where the population is a vector of individuals,
23  and the fitness is the double */
24  template<typename Ind> struct EvolutionaryAlgorithmSpace : public Space<Ind> {
25  typedef std::vector<Ind> Population;
26  };
27 
28  /** \brief the concept for evolutionary algorithm space
29  */
30  template<typename Space>
32  typedef typename Space::Individual Individual;
33  typedef typename Space::Population Population;
34  typedef typename Space::Fitness Fitness;
35 
36  BOOST_CONCEPT_USAGE(EvolutionaryAlgorithmSpaceConcept)
37  {
38  //the method to generate the fitness is required
39  Space::fitness(ind);
40  }
41  Individual ind;
42  };
43 
44  /** \brief the concept for evolutionary algorithm space
45  */
46  template<typename Space>
48  typedef typename Space::Individual Individual;
49  typedef typename Space::Population Population;
50  typedef typename Space::Fitness Fitness;
51 
53  {
54  //the method to generate the mutation of individual is required
55  Space::mutation(ind);
56  }
57  Individual ind;
58  };
59 
60  /** \brief the concept for evolutionary algorithm space
61  */
62  template<typename Space>
64  typedef typename Space::Individual Individual;
65  typedef typename Space::Population Population;
66  typedef typename Space::Fitness Fitness;
67 
69  {
70  //the method to crossover individuals required
71  Space::crossover(ind, population);
72  }
73  Individual ind;
74  Population population;
75  };
76 
77 
78  /** \brief trait tag, no transformation should be executed */
80  /** \brief trait tag, user tranformation should be executed */
82 
83 
84  /** \brief mutation policy - no mutation */
85  template<typename Space> struct MutationNone {
86 
87  /** \brief tranformation policy (none) */
89 
90  /** \brief mutation is empty operation */
91  static typename Space::Individual& mutation(typename Space::Individual& ind) {
92  return ind;
93  }
94  };
95 
96  /** \brief mutation policy - mutation from Space
97  */
98  template<typename Space> struct MutationCustom {
99 
100  /** \brief tranformation policy (custom) */
102 
103  //the methods form mutation are required
105 
106  /** \brief calls the mutation function from Space */
107  static typename Space::Individual& mutation(typename Space::Individual& ind) {
108  return Space::mutation(ind);
109  }
110  };
111 
112  /** \brief crossover policy - no crossover */
113  template<typename Space> struct CrossoverNone {
114 
115  /** \brief tranformation policy (none) */
117 
118  /** \brief crossover is empty operation */
119  static typename Space::Individual& crossover(typename Space::Individual& ind, typename Space::Population& ) {
120  return ind;
121  }
122 
123  };
124 
125  /** \brief crossover policy - crossover from Space
126  */
127  template<typename Space> struct CrossoverCustom {
128 
129  /** \brief tranformation policy (custom) */
131 
132  //the methods form mutation are required
134 
135  /** \brief calls the mutation function from Space */
136  static typename Space::Individual& crossover(typename Space::Individual& ind, typename Space::Population& pop) {
137  return Space::crossover(ind, pop);
138  }
139  };
140 
141  /** \brief succession and selection policy - the n-th best individuals survive
142  */
143  template<typename Space> struct SelectionRanking {
144 
145  /** \brief sort (partial) the population and removes the worst elements.
146  Population is returned having n elements. The function 'Space::fitness' is used.
147  */
148  static typename Space::Population& selection(typename Space::Population& population, int size) {
149 
150  typename Space::Population::iterator middle = population.begin();
151  std::advance(middle, size);
152 
153  //the n-th best elements is moved to the beginning of contener
154  std::nth_element(population.begin(), middle, population.end(),
155  boost::bind(Space::fitness, _1) > boost::bind(Space::fitness, _2) );
156 
157  population.erase( middle, population.end() );
158  return population;
159  }
160  };
161 
162  /** \brief helping class implemented the M.D.Vose (1991) algorithm */
163  class VoseAlg {
164  typedef double Fitness;
165  typedef double Probability;
166  typedef std::vector<Fitness> Fitnesses;
167  typedef std::vector<Probability> Probabilities;
168  typedef std::vector<int> Indexes;
169  public:
170 
171  VoseAlg(const Fitnesses& fitnesses)
172  : N_(static_cast<int>(fitnesses.size())),
173  gen_(0.0, static_cast<double>(N_) ),
174  prob_(N_,0.0),
175  alias_(N_,0)
176  {
177  Fitness min_fitness = std::numeric_limits<Fitness>::min();
178  Fitnesses::const_iterator ind_min = std::min_element(fitnesses.begin(), fitnesses.end() );
179  if( ind_min != fitnesses.end() )
180  min_fitness = *ind_min;
181  //Fitnesses should be non-negative -> offset all
182  Fitness sum = std::accumulate( fitnesses.begin(), fitnesses.end(), 0.0 );
183  sum -= N_* min_fitness;
184 
185  Probabilities norm_fitnesses; //the vector with sum of fitness of adjacent individuals
186  norm_fitnesses.reserve(N_);
187  if( sum < std::numeric_limits<Fitness>::epsilon() ) {
188  fill_n( std::back_inserter(norm_fitnesses), N_, 0.0 );
189  } else {
190  std::transform( fitnesses.begin(), fitnesses.end(), std::back_inserter(norm_fitnesses),
191  boost::bind( std::divides<Fitness>(), boost::bind( std::minus<Fitness>(), _1, min_fitness ), sum ) );
192  }
193 
194  //std::cout << "normalzed fitnesses " << std::endl;
195  //std::copy( norm_fitnesses.begin(), norm_fitnesses.end(),
196  // std::ostream_iterator<Fitness>(std::cout," ") );
197  //std::cout << std::endl;
198 
199  Indexes small, large;
200  const Probability one_div_n = 1.0/static_cast<double>(N_);
201  for(int index = 0; index < N_; ++index) {
202  if(norm_fitnesses[index] < one_div_n)
203  small.push_back(index);
204  else
205  large.push_back(index);
206  }
207  while(!small.empty() && !large.empty()) {
208  int j = small.back(); small.pop_back();
209  int k = large.back(); large.pop_back();
210  prob_[j] = N_ * norm_fitnesses[j];
211  alias_[j] = k;
212  norm_fitnesses[k] += norm_fitnesses[j] - one_div_n;
213  if(norm_fitnesses[k] > one_div_n)
214  large.push_back(k);
215  else {
216  small.push_back(j);
217  }
218  }
219  while(!small.empty()) {
220  prob_[small.back()] = 1.0;
221  small.pop_back();
222  }
223  while(!large.empty()) {
224  prob_[large.back()] = 1.0;
225  large.pop_back();
226  }
227 
228  }
229  int getIndexForRandom() {
230  double x = gen_();
231  int index = static_cast<int>(x); // floor of x
232  if(( x - static_cast<Probability>(index)) > prob_[index])
233  index = alias_[index];
234  return index;
235  }
236  private:
237  const int N_;
238  RandomDouble gen_;
239  Probabilities prob_;
240  Indexes alias_;
241  };
242 
243  /** \brief succession and selection policy - roulette wheel
244  (probability of selection of an idividual is equal to its normalized fitness)
245  */
246  template<typename Space> struct SelectionRoulette {
247 
248  /** \brief select individuals with probability proportional to their fitness*/
249  static typename Space::Population& selection(typename Space::Population& population, int size) {
250 
251  typedef typename Space::Population Population;
252 
253  std::vector<typename Space::Fitness> fitnesses; //the fitness of individuals
254  std::transform( population.begin(), population.end(),
255  std::back_inserter(fitnesses), boost::bind( &Space::fitness, _1 ) );
256  VoseAlg vose(fitnesses);
257  Population old_population(population); //copy of population
258  population.clear();
259 
260  std::generate_n( std::back_inserter(population), size,
261  boost::bind(&SelectionRoulette<Space>::generateIndividualFun, &old_population, &vose ) );
262  return population;
263  }
264  private:
265  //! \brief helping function (used in generate_n algorithm), to generate individual from old population)
266  static const typename Space::Individual& generateIndividualFun(const typename Space::Population* population,
267  VoseAlg* vose) {
268  int index = vose->getIndexForRandom();
269  return population->at(index);
270  }
271  };
272 
273  /** \brief the evolutionary algorithm
274 
275  \param Mutation - MutationNone, MutationCustom
276  \param Selection - SelectionRanking
277  \param StopCondition - StopAfterNSteps
278  */
279  template< typename Space,
280  template <typename> class Mutation = MutationNone,
281  template <typename> class Crossover = CrossoverNone,
282  template <typename> class Selection = SelectionRanking,
283  typename StopCondition = StopAfterNSteps<100>
284  >
286  public:
287  typedef typename Space::Individual Individual;
288  typedef typename Space::Population Population;
289  typedef typename Space::Fitness Fitness;
290 
291  //the methods for fitness calculation are required
292  BOOST_CONCEPT_ASSERT((EvolutionaryAlgorithmSpaceConcept<Space>));
293 
294  EvolutionaryAlgorithm( ) { }
295 
296  /** \brief the evolutionary algorithm - until stop repeat mutation, cross-over, selection, succession.
297  Modifies the initial population.
298  */
299  Individual& solve(Population& init_population, StopCondition stop = StopCondition() ) {
300 
301  int pop_size = static_cast<int>(init_population.size());
302  Population& current = init_population;
303  //StopCondition stop;
304 
305  do {
306  // std::cout << "population " << std::endl;
307  // std::copy( current.begin(), current.end(), std::ostream_iterator<Individual>(std::cout," ") );
308  // std::cout << std::endl;
309 
310  Population old_population(current);
311  //mutation
312  doMutation( current, typename Mutation<Space>::TransformationCategory() );
313 
314  // std::cout << "after mutation " << std::endl;
315  // std::copy( current.begin(), current.end(), std::ostream_iterator<Individual>(std::cout," ") );
316  // std::cout << std::endl;
317 
318  //crossover
319  doCrossover(current, typename Crossover<Space>::TransformationCategory() );
320 
321  // std::cout << "after crossover " << std::endl;
322  // std::copy( current.begin(), current.end(), std::ostream_iterator<Individual>(std::cout," ") );
323  // std::cout << std::endl;
324 
325 
326  //join the old and the current population
327  std::copy(old_population.begin(), old_population.end(), back_inserter(current) );
328 
329  // std::cout << "before selection " << std::endl;
330  // std::copy( current.begin(), current.end(), std::ostream_iterator<Individual>(std::cout," ") );
331  // std::cout << std::endl;
332 
333  //selection on join populations
334  Selection<Space>::selection(current, pop_size);
335 
336  // std::cout << "after selection " << std::endl;
337  // std::copy( current.begin(), current.end(), std::ostream_iterator<Individual>(std::cout," ") );
338  // std::cout << std::endl;
339 
340  stop.update(current);
341  }
342  while(! stop.isFinished() );
343 
344  typename Population::iterator best =
345  std::max_element(current.begin(), current.end(),
346  boost::bind(Space::fitness, _1) < boost::bind(Space::fitness, _2) );
347  return *best;
348  }
349  private:
350  /** \brief No action for MutationNone. */
351  void doMutation(Population&, TransformationNoneTag) { /* do nothing */ }
352  /** \brief Called when MutationCustom is given. */
353  void doMutation(Population& population, TransformationCustomTag) {
354  std::transform( population.begin(), population.end(), population.begin(),
355  boost::bind( &Mutation<Space>::mutation, _1 ) );
356  }
357  /** \brief No action for CrossoverNone. */
358  void doCrossover(Population&, TransformationNoneTag) { /* do nothing */ }
359  /** \brief Called when CrossoverCustom is given. */
360  void doCrossover(Population& population, TransformationCustomTag) {
361  std::transform(population.begin(), population.end(), population.begin(),
362  boost::bind( &Crossover<Space>::crossover, _1, population));
363  }
364 
365  };
366 
367 } //namespace search
368  } //namespace faif
369 
370 #endif // FAIF_SEARCH_EA_H
succession and selection policy - roulette wheel (probability of selection of an idividual is equal t...
Definition: EvolutionaryAlgorithm.hpp:246
the typedef-s for space for evolutionary algorithm, where the population is a vector of individuals...
Definition: EvolutionaryAlgorithm.hpp:24
the concept for evolutionary algorithm space
Definition: EvolutionaryAlgorithm.hpp:47
Definition: Chain.h:17
TransformationCustomTag TransformationCategory
tranformation policy (custom)
Definition: EvolutionaryAlgorithm.hpp:130
static Space::Population & selection(typename Space::Population &population, int size)
sort (partial) the population and removes the worst elements. Population is returned having n element...
Definition: EvolutionaryAlgorithm.hpp:148
the evolutionary algorithm
Definition: EvolutionaryAlgorithm.hpp:285
succession and selection policy - the n-th best individuals survive
Definition: EvolutionaryAlgorithm.hpp:143
crossover policy - crossover from Space
Definition: EvolutionaryAlgorithm.hpp:127
mutation policy - no mutation
Definition: EvolutionaryAlgorithm.hpp:85
static Space::Individual & mutation(typename Space::Individual &ind)
mutation is empty operation
Definition: EvolutionaryAlgorithm.hpp:91
trait tag, no transformation should be executed
Definition: EvolutionaryAlgorithm.hpp:79
mutation policy - mutation from Space
Definition: EvolutionaryAlgorithm.hpp:98
crossover policy - no crossover
Definition: EvolutionaryAlgorithm.hpp:113
the uniform distribution for double, in given range, e.g. <0,1), uses RandomSingleton ...
Definition: Random.hpp:74
the typedef-s for space, where the fitness is defined as double
Definition: Space.hpp:25
TransformationNoneTag TransformationCategory
tranformation policy (none)
Definition: EvolutionaryAlgorithm.hpp:88
Stop condition, finish the algorithm after STEPS_NUM iterations.
Definition: Space.hpp:46
TransformationNoneTag TransformationCategory
tranformation policy (none)
Definition: EvolutionaryAlgorithm.hpp:116
helping class implemented the M.D.Vose (1991) algorithm
Definition: EvolutionaryAlgorithm.hpp:163
TransformationCustomTag TransformationCategory
tranformation policy (custom)
Definition: EvolutionaryAlgorithm.hpp:101
Individual & solve(Population &init_population, StopCondition stop=StopCondition())
the evolutionary algorithm - until stop repeat mutation, cross-over, selection, succession. Modifies the initial population.
Definition: EvolutionaryAlgorithm.hpp:299
static Space::Individual & crossover(typename Space::Individual &ind, typename Space::Population &pop)
calls the mutation function from Space
Definition: EvolutionaryAlgorithm.hpp:136
trait tag, user tranformation should be executed
Definition: EvolutionaryAlgorithm.hpp:81
static Space::Population & selection(typename Space::Population &population, int size)
select individuals with probability proportional to their fitness
Definition: EvolutionaryAlgorithm.hpp:249
static Space::Individual & mutation(typename Space::Individual &ind)
calls the mutation function from Space
Definition: EvolutionaryAlgorithm.hpp:107
static Space::Individual & crossover(typename Space::Individual &ind, typename Space::Population &)
crossover is empty operation
Definition: EvolutionaryAlgorithm.hpp:119
the concept for evolutionary algorithm space
Definition: EvolutionaryAlgorithm.hpp:31
the concept for evolutionary algorithm space
Definition: EvolutionaryAlgorithm.hpp:63