PaGMO  1.1.5
sga_gray.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2015 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 #include <boost/random/uniform_int.hpp>
26 #include <boost/random/uniform_real.hpp>
27 #include <boost/random/variate_generator.hpp>
28 #include <boost/random/normal_distribution.hpp>
29 #include <boost/math/special_functions/round.hpp>
30 #include <string>
31 #include <vector>
32 #include <algorithm>
33 
34 #include "../exceptions.h"
35 #include "../population.h"
36 #include "../types.h"
37 #include "base.h"
38 #include "sga_gray.h"
39 #include "../problem/base_stochastic.h"
40 
41 namespace pagmo { namespace algorithm {
42 
44 
59 sga_gray::sga_gray(int gen, const double &cr, const double &m, int elitism, mutation::type mut, selection::type sel, crossover::type cro)
60  :base(),m_gen(gen),m_cr(cr),m_m(m),m_elitism(elitism),m_mut(mut),m_sel(sel),m_cro(cro)
61 {
62  if (gen < 0) {
63  pagmo_throw(value_error,"number of generations must be nonnegative");
64  }
65  if (cr > 1 || cr < 0) {
66  pagmo_throw(value_error,"crossover probability must be in the [0,1] range");
67  }
68  if (m < 0 || m > 1) {
69  pagmo_throw(value_error,"mutation probability must be in the [0,1] range");
70  }
71  if (elitism < 0) {
72  pagmo_throw(value_error,"elitisim must be greater or equal than zero");
73  }
74 
75  m_bit_encoding = 25;
76  m_max_encoding_integer = int(std::pow(2., m_bit_encoding));
77 }
78 
81 {
82  return base_ptr(new sga_gray(*this));
83 }
84 
86 
92 void sga_gray::evolve(population &pop) const
93 {
94  // Let's store some useful variables.
95  const problem::base &prob = pop.problem();
96  const problem::base::size_type D = prob.get_dimension(), prob_c_dimension = prob.get_c_dimension(), prob_f_dimension = prob.get_f_dimension();
97  const decision_vector &lb = prob.get_lb(), &ub = prob.get_ub();
98  const population::size_type NP = pop.size();
99 
100  //We perform some checks to determine wether the problem/population are suitable for sga_gray
101  if ( prob_c_dimension != 0 ) {
102  pagmo_throw(value_error,"The problem is not box constrained and sga_gray is not suitable to solve it");
103  }
104 
105  if ( prob_f_dimension != 1 ) {
106  pagmo_throw(value_error,"The problem is not single objective and sga_gray is not suitable to solve it");
107  }
108 
109  if (NP < 5) {
110  pagmo_throw(value_error,"for sga_gray at least 5 individuals in the population are needed");
111  }
112 
113  if (prob.get_i_dimension() > 1) {
114  pagmo_throw(value_error,"This version of SGA gray is not compatble with discrete problems.");
115  }
116 
117  // Get out if there is nothing to do.
118  if (m_gen == 0) {
119  return;
120  }
121  // Some vectors used during evolution are allocated here.
122  decision_vector dummy(D,0); //used for initialisation purposes
123  std::vector<decision_vector > X(NP,dummy), Xnew(NP,dummy);
124 
125  std::vector< std::vector<int> > chrom_vect(NP);
126 
127  std::vector<fitness_vector > fit(NP); //fitness
128 
129  fitness_vector bestfit;
130  decision_vector bestX(D,0);
131 
132  std::vector<int> selection(NP);
133 
134  // Initialise the phenotypes and their fitness to that of the initial deme
135  for (pagmo::population::size_type i = 0; i<NP; i++ ) {
136  X[i] = pop.get_individual(i).cur_x;
137  fit[i] = pop.get_individual(i).cur_f;
138  }
139 
140  // Find the best member and store in bestX and bestfit
141  double bestidx = pop.get_best_idx();
142  bestX = pop.get_individual(bestidx).cur_x;
143  bestfit = pop.get_individual(bestidx).cur_f;
144 
145  // Main sga_gray loop
146  for(int j=0; j<m_gen; j++) {
147 
148  selection = this->selection(fit,prob);
149 
150  // Xnew stores the new selected generation of genotypes
151  for(population::size_type i = 0; i < NP; i++) {
152  chrom_vect[i] = this->encode(X.at(selection.at(i)), lb, ub);
153  }
154 
155  this->crossover(chrom_vect);
156  this->mutate(chrom_vect);
157 
158  //Xnew stores the new selected generation of genotypes
159  for(population::size_type i=0; i<NP; i++) {
160  Xnew[i] = this->decode(chrom_vect.at(i), lb, ub);
161  }
162 
163  // If the problem is a stochastic optimization chage the seed and re-evaluate taking care to update also best and local bests
164  try
165  {
166  //4 - Evaluate the new population (stochastic problem)
167  dynamic_cast<const pagmo::problem::base_stochastic &>(prob).set_seed(m_urng());
168  pop.clear(); // Removes memory based on different seeds (champion and best_x, best_f, best_c)
169 
170  // We re-evaluate the best individual (for elitism)
171  prob.objfun(bestfit,bestX);
172  // Re-evaluate wrt new seed the particle position and memory
173  for (pagmo::population::size_type i = 0; i < NP;i++) {
174  // We evaluate here the new individual fitness
175  prob.objfun(fit[i],Xnew[i]);
176  // We update the velocity (in case coupling with PSO via archipelago)
177  //dummy = Xnew[i];
178  //std::transform(dummy.begin(), dummy.end(), pop.get_individual(i).cur_x.begin(), dummy.begin(),std::minus<double>());
180  pop.push_back(Xnew[i]);
181  //pop.set_v(i,dummy);
182  if (prob.compare_fitness(fit[i], bestfit)) {
183  bestfit = fit[i];
184  bestX = Xnew[i];
185  }
186  }
187  }
188  catch (const std::bad_cast& e)
189  {
190  //4 - Evaluate the new population (deterministic problem)
191  for (pagmo::population::size_type i = 0; i < NP;i++) {
192  prob.objfun(fit[i],Xnew[i]);
193  dummy = Xnew[i];
194  std::transform(dummy.begin(), dummy.end(), pop.get_individual(i).cur_x.begin(), dummy.begin(),std::minus<double>());
195  //updates x and v (cache avoids to recompute the objective function and constraints)
196  pop.set_x(i,Xnew[i]);
197  pop.set_v(i,dummy);
198  if (prob.compare_fitness(fit[i], bestfit)) {
199  bestfit = fit[i];
200  bestX = Xnew[i];
201  }
202  }
203  }
204 
205  //5 - Reinsert best individual every m_elitism generations
206  if ((m_elitism != 0) && (j % m_elitism == 0) ) {
207  int worst=0;
208  for (pagmo::population::size_type i = 1; i < NP;i++) {
209  if ( prob.compare_fitness(fit[worst],fit[i]) ) worst=i;
210  }
211  Xnew[worst] = bestX;
212  fit[worst] = bestfit;
213  dummy = Xnew[worst];
214  std::transform(dummy.begin(), dummy.end(), pop.get_individual(worst).cur_x.begin(), dummy.begin(),std::minus<double>());
215  //updates x and v (cache avoids to recompute the objective function)
216  pop.set_x(worst,Xnew[worst]);
217  pop.set_v(worst,dummy);
218  }
219  X = Xnew;
220  } // end of main sga_gray loop
221 }
222 
224 std::string sga_gray::get_name() const
225 {
226  return "A Simple Genetic Algorithm with Gray encoding";
227 }
228 
230 
234 {
235  std::ostringstream s;
236  s << "gen:" << m_gen << ' ';
237  s << "CR:" << m_cr << ' ';
238  s << "M:" << m_m << ' ';
239  s << "elitism:" << m_elitism << ' ';
240  s << "mutation:";
241  switch (m_mut) {
242  case mutation::UNIFORM: {
243  s << "UNIFORM ";
244  break;
245  }
246  }
247  s << "selection:";
248  switch (m_sel) {
249  case selection::BEST20: {
250  s << "BEST20 ";
251  break;
252  }
253  case selection::ROULETTE: {
254  s << "ROULETTE ";
255  break;
256  }
257  }
258  s << "crossover:";
259  switch (m_cro) {
260  case crossover::SINGLE_POINT: {
261  s << "SINGLE_POINT ";
262  break;
263  }
264  }
265 
266  return s.str();
267 }
268 
270 
277 std::vector<int> sga_gray::selection(const std::vector<fitness_vector> &pop_f, const problem::base &prob) const
278 {
279  population::size_type NP = pop_f.size();
280 
281  std::vector<int> selection(NP);
282 
283  switch (m_sel) {
284  case selection::BEST20: {
285  //selects the best 20% and puts multiple copies in Xnew
286  // in practice, for performance reasons, the selection, fitnessID should not be created each time
287  // thus we might prefer to use a class of operators
288  int tempID;
289  std::vector<int> fitnessID(NP);
290 
291  for (population::size_type i=0; i<NP; i++) {
292  fitnessID[i]=i;
293  }
294  for (population::size_type i=0; i < (NP-1); ++i) {
295  for (population::size_type j=i+1; j<NP; ++j) {
296  if( prob.compare_fitness(pop_f[fitnessID[j]],pop_f[fitnessID[i]]) ) {
297  //if ( pop_f[fitnessID[j]] < pop_f[fitnessID[i]]) {
298  //swap fitness values
299  // fit[i].swap(fit[j]);
300  //swap id's
301  tempID = fitnessID[i];
302  fitnessID[i] = fitnessID[j];
303  fitnessID[j] = tempID;
304  }
305  }
306  }
307 
308  // fitnessID now contains the position of individuals ranked from best to worst
309  int best20 = NP/5;
310  for (pagmo::population::size_type i=0; i<NP; ++i) {
311  selection[i] = fitnessID[i % best20]; // multiple copies
312  }
313  break;
314  }
315  case selection::ROULETTE: { // ROULETTE
316  std::vector<double> selectionfitness(NP), cumsum(NP), cumsumTemp(NP);
317 
318  //We scale all fitness values from 0 (worst) to absolute value of the best fitness
319  fitness_vector worstfit=pop_f[0];
320  for (population::size_type i = 1; i < NP;i++) {
321  if (prob.compare_fitness(worstfit,pop_f[i])) {
322  worstfit=pop_f[i];
323  }
324  }
325 
326  for (population::size_type i = 0; i < NP; i++) {
327  selectionfitness[i] = fabs(worstfit[0] - pop_f[i][0]);
328  }
329 
330  // We build and normalise the cumulative sum
331  cumsumTemp[0] = selectionfitness[0];
332  for (population::size_type i = 1; i< NP; i++) {
333  cumsumTemp[i] = cumsumTemp[i - 1] + selectionfitness[i];
334  }
335  for (population::size_type i = 0; i < NP; i++) {
336  cumsum[i] = cumsumTemp[i]/cumsumTemp[NP-1];
337  }
338 
339  //we throw a dice and pick up the corresponding index
340  double r2;
341  for (population::size_type i = 0; i < NP; i++) {
342  r2 = m_drng();
343  for (population::size_type j = 0; j < NP; j++) {
344  if (cumsum[j] > r2) {
345  selection[i]=j;
346  break;
347  }
348  }
349  }
350  break;
351  }
352  }
353  return selection;
354 }
355 
357 
362 void sga_gray::crossover(std::vector< std::vector<int> > &pop_x) const
363 {
364  population::size_type NP = pop_x.size();
365 
366  // chromosome dimension
367  fitness_vector::size_type D = pop_x.at(0).size();
368 
369  std::vector<population::size_type> mating_pool(0);
370  // creates the mating pool
371  for(population::size_type i=0; i<NP; i++) {
372  if (m_drng() < m_m) {
373  mating_pool.push_back(i);
374  }
375  }
376  population::size_type mating_pool_size = mating_pool.size();
377 
378  if (mating_pool_size % 2 != 0) {
379  // we remove or add one individual
380  if(m_drng() < 0.5) {
381  // add one randomly selected
382  mating_pool.push_back(boost::uniform_int<int>(0,mating_pool_size - 1)(m_urng));
383  } else {
384  mating_pool.erase(mating_pool.begin()+boost::uniform_int<int>(0,mating_pool_size - 1)(m_urng));
385  }
386  }
387  // update the mating pool size
388  mating_pool_size = mating_pool.size();
389 
390  if(mating_pool_size == 0) {
391  return;
392  }
393 
394  switch (m_cro) {
395  //0 - single point crossover
396  case crossover::SINGLE_POINT:
397  {
398  // random mating of the individuals
399  for (population::size_type i=0; i<mating_pool_size/2; i++) {
400  // we randomly select the individuals
401  std::vector<int> &member1 = pop_x[mating_pool[i*2]];
402  std::vector<int> &member2 = pop_x[mating_pool[i*2 + 1]];
403 
404  // we mate them at a random position
405  int position = boost::uniform_int<int>(1, D-1)(m_urng);
406 
407  std::swap_ranges(member1.begin(), member1.begin()+position, member2.begin());
408  }
409  break;
410  }
411  }
412 }
413 
415 
420 void sga_gray::mutate(std::vector< std::vector<int> > &pop_x) const
421 {
422  const problem::base::size_type D = pop_x.at(0).size();
423  const population::size_type NP = pop_x.size();
424 
425  switch (m_mut) {
426  case mutation::UNIFORM: {
427  for (population::size_type i=0; i<NP; i++) {
428  for (problem::base::size_type j=0; j<D; j++) {
429  if (m_drng() < m_m) {
430  pop_x[i][j] = pop_x[i][j] == 0 ? 1:0;//boost::uniform_int<int>(0,1)(m_urng);
431  }
432  }
433  }
434  break;
435  }
436  }
437 }
438 
440 
447 std::vector<int> sga_gray::double_to_binary(const double &number, const double &lb, const double &ub) const
448 {
449  // initialize the vector of size m_bit_encoding
450  std::vector<int> binary(m_bit_encoding, 0);
451 
452  // convert the current number into its binary representation considering the domain
453  // available
454  int temp_number = (number - lb) * (m_max_encoding_integer - 1) / (ub - lb) ;
455 
456  // store the binary number
457  int position = 0;
458  while (temp_number!=0)
459  {
460  if( (temp_number % 2) == 0 ) {
461  binary[position] = 0;
462  } else {
463  binary[position] = 1;
464  }
465  temp_number = (int)std::floor(temp_number/2);
466  position++;
467  }
468  // reverse the order as this algorithm provides the reverse binary reprentation
469  std::reverse(binary.begin(),binary.end());
470 
471  return binary;
472 }
473 
475 
481 double sga_gray::binary_to_double(const std::vector<int> &binary, const double &lb, const double &ub) const
482 {
483  // find the representation of the binary number in the integer domain
484  int temp_number = 0;
485  for(int i=0; i<m_bit_encoding; i++) {
486  temp_number += binary.at(m_bit_encoding - 1 - i) * std::pow(2.,i);
487  }
488 
489  // rescaling back into the domain double domain
490 
491  return temp_number * (ub - lb) / (m_max_encoding_integer - 1) + lb;
492 }
493 
495 
499 std::vector<int> sga_gray::gray_to_binary(const std::vector<int> &gray) const
500 {
501  // uses the XOR table
502  int length = gray.size();
503 
504  std::vector<int> binary = gray;
505 
506  // the MSB is the same
507  for(int i=1; i<length; i++) {
508  binary[i] = (binary[i-1]+gray[i]) % 2;
509  }
510 
511  return binary;
512 }
513 
515 
519 std::vector<int> sga_gray::binary_to_gray(const std::vector<int> &binary) const
520 {
521  int length = binary.size();
522  std::vector<int> gray = binary;
523 
524  // the MSB is the same
525  // uses the XOR table
526  for(int i = 1; i < length; i++) {
527  gray[i] = binary[i]^binary[i-1];
528  }
529 
530  return gray;
531 }
532 
534 
540 std::vector<int> sga_gray::encode(const decision_vector &x, const decision_vector &lb, const decision_vector &ub) const
541 {
542  std::vector<int> encoded_x(x.size() * m_bit_encoding, 0);
543 
544  for(decision_vector::size_type i=0; i<x.size(); i++) {
545  std::vector<int> encoded_gene = binary_to_gray(double_to_binary(x.at(i),lb.at(i),ub.at(i)));
546  // copy the gene at the right location
547  for(int j=0; j<m_bit_encoding; j++) {
548  encoded_x[i*m_bit_encoding + j] = encoded_gene.at(j);
549  }
550  }
551 
552  return encoded_x;
553 }
554 
556 
562 decision_vector sga_gray::decode(const std::vector<int> &chrom, const decision_vector &lb, const decision_vector &ub) const
563 {
564  decision_vector decoded_x(chrom.size() / m_bit_encoding, 0.);
565 
566  for(decision_vector::size_type i=0; i<decoded_x.size(); i++) {
567  // extract the part that need to be decoded
568  std::vector<int> encoded_gene(m_bit_encoding);
569 
570  for(int j=0; j<m_bit_encoding; j++) {
571  encoded_gene[j] = chrom.at(i*m_bit_encoding + j);
572  }
573 
574  decoded_x[i] = binary_to_double(gray_to_binary(encoded_gene),lb.at(i),ub.at(i));
575  }
576 
577  return decoded_x;
578 }
579 
580 }} //namespaces
581 
582 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::sga_gray)
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
Root PaGMO namespace.
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
fitness_vector cur_f
Current fitness vector.
Definition: population.h:89
type
Mutation type, uniform.
Definition: sga_gray.h:61
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
Base algorithm class.
Base problem class.
Definition: problem/base.h:148
void evolve(population &) const
Evolve implementation.
Definition: sga_gray.cpp:92
Population class.
Definition: population.h:70
size_type get_dimension() const
Return global dimension.
bool compare_fitness(const fitness_vector &, const fitness_vector &) const
Compare fitness vectors.
base_ptr clone() const
Clone method.
Definition: sga_gray.cpp:80
fitness_vector objfun(const decision_vector &) const
Return fitness of pagmo::decision_vector.
size_type get_i_dimension() const
Return integer dimension.
sga_gray(int gen=1, const double &cr=.95, const double &m=.02, int elitism=1, mutation::type mut=mutation::UNIFORM, selection::type sel=selection::ROULETTE, crossover::type cro=crossover::SINGLE_POINT)
Constructor.
Definition: sga_gray.cpp:59
type
Selection type, best 20% or roulette.
Definition: sga_gray.h:56
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
c_size_type get_c_dimension() const
Return global constraints dimension.
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: sga_gray.cpp:233
const decision_vector & get_ub() const
Upper bounds getter.
Base Stochastic Optimization Problem.
The Simple Genetic Algorithm with gray encoding (sga_gray)
Definition: sga_gray.h:50
type
Crossover type, single point.
Definition: sga_gray.h:67
container_type::size_type size_type
Population size type.
Definition: population.h:192
decision_vector cur_x
Current decision vector.
Definition: population.h:83
rng_uint32 m_urng
Random number generator for unsigned integer values.
f_size_type get_f_dimension() const
Return fitness dimension.
const decision_vector & get_lb() const
Lower bounds getter.
rng_double m_drng
Random number generator for double-precision floating point values.
Crossover operator info.
Definition: sga_gray.h:65
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160
std::string get_name() const
Algorithm name.
Definition: sga_gray.cpp:224