PaGMO  1.1.5
vega.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 
33 #include "../exceptions.h"
34 #include "../population.h"
35 #include "../problem/base.h"
36 #include "../types.h"
37 #include "vega.h"
38 #include "../problem/base_stochastic.h"
39 #include "../problem/decompose.h"
40 
41 namespace pagmo { namespace algorithm {
42 
44 
58 vega::vega(int gen, const double &cr, const double &m, int elitism, mutation::type mut, double width, crossover::type cro)
59  :base(),m_gen(gen),m_cr(cr),m_m(m),m_elitism(elitism),m_mut(mut,width),m_cro(cro)
60 {
61  if (gen < 0) {
62  pagmo_throw(value_error,"number of generations must be nonnegative");
63  }
64  if (cr > 1 || cr < 0) {
65  pagmo_throw(value_error,"crossover probability must be in the [0,1] range");
66  }
67  if (m < 0 || m > 1) {
68  pagmo_throw(value_error,"mutation probability must be in the [0,1] range");
69  }
70  if (elitism < 1) {
71  pagmo_throw(value_error,"elitisim must be greater than zero");
72  }
73  if (width <0 || width >1) {
74  pagmo_throw(value_error,"mutation width must be in the [0,1] range");
75  }
76 }
77 
80 {
81  return base_ptr(new vega(*this));
82 }
83 
85 
91 void vega::evolve(population &pop) const
92 {
93  // Let's store some useful variables.
94  const problem::base &prob = pop.problem();
95  const problem::base::size_type D = prob.get_dimension(), Di = prob.get_i_dimension(), prob_c_dimension = prob.get_c_dimension(), prob_f_dimension = prob.get_f_dimension();
96  const decision_vector &lb = prob.get_lb(), &ub = prob.get_ub();
97  const population::size_type NP = pop.size();
98  const problem::base::size_type Dc = D - Di;
99 
100  //We perform some checks to determine wether the problem/population are suitable for VEGA
101  if( prob_c_dimension != 0 ) {
102  pagmo_throw(value_error,"The problem is not box constrained and VEGA is not suitable to solve it");
103  }
104 
105  if( prob_f_dimension < 2 ) {
106  pagmo_throw(value_error,"The problem is not multiobjective and VEGA is not suitable to solve it");
107  }
108 
109  if(NP < 5*prob_f_dimension) {
110  pagmo_throw(value_error,"for VEGA at least 5 * number of objectives individuals in the population are needed");
111  }
112 
113  if( (NP%prob_f_dimension != 0) && (NP != 1) ) {
114  pagmo_throw(value_error,"for VEGA the population size must a multiple of the number of objectives");
115  }
116 
117  // sub population size
118  population::size_type sub_pop_size = NP/prob_f_dimension;
119 
120  // Get out if there is nothing to do.
121  if (m_gen == 0) {
122  return;
123  }
124 
125  // Some vectors used during evolution are allocated here.
126  decision_vector dummy(D,0); //used for initialisation purposes
127  std::vector<decision_vector > X(NP,dummy);
128  std::vector<decision_vector > Xnew(NP,dummy);
129  // fitness for the main problem
130  std::vector<fitness_vector > fit(NP);
131 
132  // selection vectors
133  std::vector<double> selectionfitness(sub_pop_size);
134  std::vector<double> cumsum(sub_pop_size);
135  std::vector<double> cumsumTemp(sub_pop_size);
136 
137  std::vector<population::size_type> selection(sub_pop_size);
138 
139  // Initialise the chromosomes and their fitness to that of the initial deme
140  for (pagmo::population::size_type i = 0; i<NP; i++ ) {
141  X[i] = pop.get_individual(i).cur_x;
142  fit[i] = pop.get_individual(i).cur_f;
143  }
144 
145  // Find the best member and store in bestX and bestfit
146  fitness_vector bestfit;
147  decision_vector bestX(D,0);
148  population::size_type bestidx = pop.get_best_idx();
149  bestX = pop.get_individual(bestidx).cur_x;
150  bestfit = pop.get_individual(bestidx).cur_f;
151 
152  // creates new sub problems using the decompose meta-problem
153  std::vector<problem::base_ptr> sub_probs;
154  std::vector< std::vector<decision_vector> > sub_x(prob_f_dimension);
155  std::vector< std::vector<fitness_vector> > sub_f(prob_f_dimension);
156 
157  // generating the subproblems used to evaluate fitnesses
158  for(unsigned int i=0; i<prob_f_dimension; i++) {
159  std::vector<double> sub_prob_weights(prob_f_dimension,0.);
160  sub_prob_weights[i] = 1.;
161  sub_probs.push_back(problem::decompose(prob, problem::decompose::WEIGHTED, sub_prob_weights).clone());
162  }
163 
164  // Main VEGA loop
165  for(int j=0; j<m_gen; j++) {
166 
167  boost::uniform_int<int> pop_idx(0,NP-1);
168  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > p_idx(m_urng,pop_idx);
169 
170  //We create some pseudo-random permutation of the poulation indexes
171  std::random_shuffle(X.begin(),X.end(),p_idx);
172 
173  // Initialise the fitness with each problem
174  for(unsigned int sp_idx=0; sp_idx<prob_f_dimension; sp_idx++) {
175  problem::base_ptr current_sub_prob = sub_probs.at(sp_idx);
176  std::vector<fitness_vector> &current_sub_f = sub_f[sp_idx];
177  std::vector<decision_vector> &current_sub_x = sub_x[sp_idx];
178 
179  current_sub_f = std::vector<fitness_vector>(sub_pop_size);
180  current_sub_x = std::vector<decision_vector>(sub_pop_size);
181 
182  population::size_type sub_pop_begin_idx = sp_idx*sub_pop_size;
183 
184  // for each individual of the current subpopulation
185  for(pagmo::population::size_type k=0; k<sub_pop_size; k++) {
186  // initialize design and fitness vectors
187  current_sub_x[k] = X.at(sub_pop_begin_idx + k);
188  current_sub_f[k] = current_sub_prob->objfun(current_sub_x[k]);
189  }
190  }
191 
192  // loop over the sub problems to do the selection
193  for(unsigned int sp_idx=0; sp_idx<prob_f_dimension; sp_idx++) {
194 
195  std::vector<fitness_vector> &current_sub_f = sub_f[sp_idx];
196  problem::base_ptr current_sub_prob = sub_probs.at(sp_idx);
197 
198  // ---------------------------
199  // scaled roulette
200  // selection of the best individuals among the current sub population
201  // checked and seems to be correctly implemented
202  // ---------------------------
203 
204  // We scale all fitness values from 0 (worst) to absolute value of the best fitness
205  fitness_vector worstfit = current_sub_f[0];
206  for(pagmo::population::size_type i=1; i < sub_pop_size; i++) {
207  if(current_sub_prob->compare_fitness(worstfit, current_sub_f[i]))
208  worstfit = current_sub_f[i];
209  }
210 
211  for(pagmo::population::size_type i=0; i < sub_pop_size; i++) {
212  selectionfitness[i] = fabs(worstfit[0] - current_sub_f[i][0]);
213  }
214 
215  // We build and normalise the cumulative sum
216  cumsumTemp[0] = selectionfitness[0];
217 
218  for(pagmo::population::size_type i=1; i < sub_pop_size; i++) {
219  cumsumTemp[i] = cumsumTemp[i - 1] + selectionfitness[i];
220  }
221  for(pagmo::population::size_type i=0; i < sub_pop_size; i++) {
222  cumsum[i] = cumsumTemp[i] / cumsumTemp[sub_pop_size-1];
223  }
224 
225  //we throw a dice and pick up the corresponding index
226  double r2;
227  for(pagmo::population::size_type i=0; i < sub_pop_size; i++) {
228  r2 = m_drng();
229  for(pagmo::population::size_type l=0; l < sub_pop_size; l++) {
230  if(cumsum[l] > r2) {
231  selection[i] = l;
232  break;
233  }
234  }
235  }
236  // ---------------------------
237  // end of roulette selection
238  // ---------------------------
239 
240  // We have the selected individuals in the sub population
241  // we can now store the current individuals in the mating pool
242  population::size_type sub_pop_begin_idx = sp_idx * sub_pop_size;
243  std::vector<decision_vector> &current_sub_x = sub_x[sp_idx];
244 
245  // Xnew stores the new selected generation of chromosomes
246  for (pagmo::population::size_type i=0; i < sub_pop_size; i++) {
247  Xnew[sub_pop_begin_idx + i] = current_sub_x[selection[i]];
248  }
249  }
250 
251  //2 - Crossover
252  {
253  int r1, L;
254  decision_vector member1, member2;
255 
256  for(pagmo::population::size_type i=0; i < NP; i++) {
257  // for each chromosome selected i.e. in Xnew
258  member1 = Xnew[i];
259  // we select a mating partner different from the self (i.e. no masturbation)
260  do {
261  r1 = boost::uniform_int<int>(0,NP - 1)(m_urng);
262  } while ( r1 == boost::numeric_cast<int>(i) );
263  member2 = Xnew[r1];
264  // and we operate crossover
265  switch (m_cro) {
266  // 0 - binomial crossover
267  case crossover::BINOMIAL: {
268  size_t n = boost::uniform_int<int>(0,D-1)(m_urng);
269  for (size_t L = 0; L < D; ++L) { /* perform D binomial trials */
270  if ((m_drng() < m_cr) || L + 1 == D) { /* change at least one parameter */
271  member1[n] = member2[n];
272  }
273  n = (n+1)%D;
274  }
275  break; }
276  // 1 - exponential crossover
277  case crossover::EXPONENTIAL: {
278  size_t n = boost::uniform_int<int>(0,D-1)(m_urng);
279  L = 0;
280  do {
281  member1[n] = member2[n];
282  n = (n+1) % D;
283  L++;
284  } while ( (m_drng() < m_cr) && (L < boost::numeric_cast<int>(D)) );
285  break; }
286  }
287  Xnew[i] = member1;
288  }
289  }
290 
291  //3 - Mutation
292  switch (m_mut.m_type) {
293  case mutation::GAUSSIAN: {
294  boost::normal_distribution<double> dist;
295  boost::variate_generator<boost::lagged_fibonacci607 &, boost::normal_distribution<double> > delta(m_drng,dist);
296  for (pagmo::problem::base::size_type k = 0; k < Dc;k++) { //for each continuous variable
297  double std = (ub[k]-lb[k]) * m_mut.m_width;
298  for (pagmo::population::size_type i = 0; i < NP;i++) { //for each individual
299  if (m_drng() < m_m) {
300  double mean = Xnew[i][k];
301  double tmp = (delta() * std + mean);
302  if ( (tmp < ub[k]) && (tmp > lb[k]) ) Xnew[i][k] = tmp;
303  }
304  }
305  }
306  for (pagmo::problem::base::size_type k = Dc; k < D;k++) { //for each integer variable
307  double std = (ub[k]-lb[k]) * m_mut.m_width;
308  for (pagmo::population::size_type i = 0; i < NP;i++) { //for each individual
309  if (m_drng() < m_m) {
310  double mean = Xnew[i][k];
311  double tmp = boost::math::iround(delta() * std + mean);
312  if ( (tmp < ub[k]) && (tmp > lb[k]) ) Xnew[i][k] = tmp;
313  }
314  }
315  }
316  break;
317  }
318  case mutation::RANDOM: {
319  for (pagmo::population::size_type i = 0; i < NP;i++) {
320  for (pagmo::problem::base::size_type j = 0; j < Dc;j++) { //for each continuous variable
321  if (m_drng() < m_m) {
322  Xnew[i][j] = boost::uniform_real<double>(lb[j],ub[j])(m_drng);
323  }
324  }
325  for (pagmo::problem::base::size_type j = Dc; j < D;j++) {//for each integer variable
326  if (m_drng() < m_m) {
327  Xnew[i][j] = boost::uniform_int<int>(lb[j],ub[j])(m_urng);
328  }
329  }
330  }
331  break;
332  }
333  }
334 
335  // If the problem is a stochastic optimization change the seed and re-evaluate taking care to update also best and local bests
336  try
337  {
338  //4 - Evaluate the new population (stochastic problem)
339  dynamic_cast<const pagmo::problem::base_stochastic &>(prob).set_seed(m_urng());
340  pop.clear(); // Removes memory based on different seeds (champion and best_x, best_f, best_c)
341 
342  // We re-evaluate the best individual (for elitism)
343  prob.objfun(bestfit,bestX);
344  // Re-evaluate wrt new seed the particle position and memory
345  for (pagmo::population::size_type i=0; i < NP; i++) {
346  // We evaluate here the new individual fitness
347  prob.objfun(fit[i],Xnew[i]);
348  // We update the velocity (in case coupling with PSO via archipelago)
349  //dummy = Xnew[i];
350  //std::transform(dummy.begin(), dummy.end(), pop.get_individual(i).cur_x.begin(), dummy.begin(),std::minus<double>());
352  pop.push_back(Xnew[i]);
353  //pop.set_v(i,dummy);
354  if (prob.compare_fitness(fit[i], bestfit)) {
355  bestfit = fit[i];
356  bestX = Xnew[i];
357  }
358  }
359  }
360  catch (const std::bad_cast& e)
361  {
362  //4 - Evaluate the new population (deterministic problem)
363  for (pagmo::population::size_type i=0; i < NP; i++) {
364  prob.objfun(fit[i],Xnew[i]);
365  dummy = Xnew[i];
366  std::transform(dummy.begin(), dummy.end(), pop.get_individual(i).cur_x.begin(), dummy.begin(),std::minus<double>());
367  //updates x and v (cache avoids to recompute the objective function and constraints)
368  pop.set_x(i,Xnew[i]);
369  pop.set_v(i,dummy);
370  if (prob.compare_fitness(fit[i], bestfit)) {
371  bestfit = fit[i];
372  bestX = Xnew[i];
373  }
374  }
375  }
376 
377  // need to check if elitism is suitable with MO
378 
379  //5 - Reinsert best individual every m_elitism generations
380  if (j % m_elitism == 0) {
381  int worst=0;
382  for (pagmo::population::size_type i = 1; i < NP;i++) {
383  if ( prob.compare_fitness(fit[worst],fit[i]) ) worst=i;
384  }
385  Xnew[worst] = bestX;
386  fit[worst] = bestfit;
387  dummy = Xnew[worst];
388  std::transform(dummy.begin(), dummy.end(), pop.get_individual(worst).cur_x.begin(), dummy.begin(),std::minus<double>());
389  //updates x and v (cache avoids to recompute the objective function)
390  pop.set_x(worst,Xnew[worst]);
391  pop.set_v(worst,dummy);
392  }
393 
394  // updating the design vectors
395  X = Xnew;
396  } // end of main VEGA loop
397 }
398 
400 std::string vega::get_name() const
401 {
402  return "Vector evaluated genetic algorithm (VEGA)";
403 }
404 
406 
409 std::string vega::human_readable_extra() const
410 {
411  std::ostringstream s;
412  s << "gen:" << m_gen << ' ';
413  s << "CR:" << m_cr << ' ';
414  s << "M:" << m_m << ' ';
415  s << "elitism:" << m_elitism << ' ';
416  s << "mutation:";
417  switch (m_mut.m_type) {
418  case mutation::RANDOM: {
419  s << "RANDOM ";
420  break;
421  }
422  case mutation::GAUSSIAN: {
423  s << "GAUSSIAN (" << m_mut.m_width << ") ";
424  break;
425  }
426  }
427  s << "crossover:";
428  switch (m_cro) {
429  case crossover::EXPONENTIAL: {
430  s << "EXPONENTIAL ";
431  break;
432  }
433  case crossover::BINOMIAL: {
434  s << "BINOMIAL ";
435  break;
436  }
437  }
438 
439  return s.str();
440 }
441 
442 //pagmo::population::size_type vega::roulette_selection(pagmo::population::size_type idx1, pagmo::population::size_type idx2, const pagmo::population& pop) const
443 //{
444 
445 //}
446 
447 }} //namespaces
448 
449 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::vega)
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
Root PaGMO namespace.
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base problem.
Definition: problem/base.h:62
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
fitness_vector cur_f
Current fitness vector.
Definition: population.h:89
Decompose meta-problem.
Definition: decompose.h:62
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
type m_type
Mutation type.
Definition: vega.h:67
vega(int gen=1, const double &cr=.95, const double &m=.02, int elitism=1, mutation::type mut=mutation::GAUSSIAN, double width=0.1, crossover::type cro=crossover::EXPONENTIAL)
Constructor.
Definition: vega.cpp:58
Base algorithm class.
STL namespace.
Base problem class.
Definition: problem/base.h:148
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: vega.cpp:409
Population class.
Definition: population.h:70
size_type get_dimension() const
Return global dimension.
std::string get_name() const
Algorithm name.
Definition: vega.cpp:400
bool compare_fitness(const fitness_vector &, const fitness_vector &) const
Compare fitness vectors.
fitness_vector objfun(const decision_vector &) const
Return fitness of pagmo::decision_vector.
VEGA based multi-objective algorithm.
Definition: vega.h:51
size_type get_i_dimension() const
Return integer dimension.
type
Mutation type, gaussian or random.
Definition: vega.h:57
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
c_size_type get_c_dimension() const
Return global constraints dimension.
const decision_vector & get_ub() const
Upper bounds getter.
Base Stochastic Optimization Problem.
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.
The fitness function is the weighted sum of the multiple original fitnesses.
Definition: decompose.h:67
const decision_vector & get_lb() const
Lower bounds getter.
double m_width
Mutation width.
Definition: vega.h:69
rng_double m_drng
Random number generator for double-precision floating point values.
void evolve(population &) const
Evolve implementation.
Definition: vega.cpp:91
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160
type
Crossover type, binomial or exponential.
Definition: vega.h:83
base_ptr clone() const
Clone method.
Definition: vega.cpp:79