PaGMO  1.1.5
de.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 <string>
28 #include <vector>
29 
30 #include "../exceptions.h"
31 #include "../population.h"
32 #include "../types.h"
33 #include "base.h"
34 #include "de.h"
35 
36 namespace pagmo { namespace algorithm {
37 
39 
50 de::de(int gen, double f, double cr, int strategy, double ftol, double xtol):base(),m_gen(gen),m_f(f),m_cr(cr),m_strategy(strategy),m_ftol(ftol),m_xtol(xtol) {
51  if (gen < 0) {
52  pagmo_throw(value_error,"number of generations must be nonnegative");
53  }
54  if (strategy < 1 || strategy > 10) {
55  pagmo_throw(value_error,"strategy index must be one of 1 ... 10");
56  }
57  if (cr < 0 || f < 0 || cr > 1 || f > 1) {
58  pagmo_throw(value_error,"the f and cr parameters must be in the [0,1] range");
59  }
60 }
61 
64 {
65  return base_ptr(new de(*this));
66 }
67 
69 
76 void de::evolve(population &pop) const
77 {
78  // Let's store some useful variables.
79  const problem::base &prob = pop.problem();
80  const problem::base::size_type D = prob.get_dimension(), prob_i_dimension = prob.get_i_dimension(), prob_c_dimension = prob.get_c_dimension(), prob_f_dimension = prob.get_f_dimension();
81  const decision_vector &lb = prob.get_lb(), &ub = prob.get_ub();
82  const population::size_type NP = pop.size();
83  const problem::base::size_type Dc = D - prob_i_dimension;
84 
85 
86  //We perform some checks to determine wether the problem/population are suitable for DE
87  if ( Dc == 0 ) {
88  pagmo_throw(value_error,"There is no continuous part in the problem decision vector for DE to optimise");
89  }
90 
91  if ( prob_c_dimension != 0 ) {
92  pagmo_throw(value_error,"The problem is not box constrained and DE is not suitable to solve it");
93  }
94 
95  if ( prob_f_dimension != 1 ) {
96  pagmo_throw(value_error,"The problem is not single objective and DE is not suitable to solve it");
97  }
98 
99  if (NP < 6) {
100  pagmo_throw(value_error,"for DE at least 6 individuals in the population are needed");
101  }
102 
103  // Get out if there is nothing to do.
104  if (m_gen == 0) {
105  return;
106  }
107  // Some vectors used during evolution are allocated here.
108  decision_vector dummy(D), tmp(D); //dummy is used for initialisation purposes, tmp to contain the mutated candidate
109  std::vector<decision_vector> popold(NP,dummy), popnew(NP,dummy);
110  decision_vector gbX(D),gbIter(D);
111  fitness_vector newfitness(prob_f_dimension); //new fitness of the mutaded candidate
112  fitness_vector gbfit(prob_f_dimension); //global best fitness
113  std::vector<fitness_vector> fit(NP,gbfit);
114 
115  //We extract from pop the chromosomes and fitness associated
116  for (std::vector<double>::size_type i = 0; i < NP; ++i) {
117  popold[i] = pop.get_individual(i).cur_x;
118  fit[i] = pop.get_individual(i).cur_f;
119  }
120  popnew = popold;
121 
122  // Initialise the global bests
123  gbX=pop.champion().x;
124  gbfit=pop.champion().f;
125  // container for the best decision vector of generation
126  gbIter = gbX;
127 
128  // Main DE iterations
129  size_t r1,r2,r3,r4,r5; //indexes to the selected population members
130  for (int gen = 0; gen < m_gen; ++gen) {
131  //Start of the loop through the deme
132  for (size_t i = 0; i < NP; ++i) {
133  do { /* Pick a random population member */
134  /* Endless loop for NP < 2 !!! */
135  r1 = boost::uniform_int<int>(0,NP-1)(m_urng);
136  } while (r1==i);
137 
138  do { /* Pick a random population member */
139  /* Endless loop for NP < 3 !!! */
140  r2 = boost::uniform_int<int>(0,NP-1)(m_urng);
141  } while ((r2==i) || (r2==r1));
142 
143  do { /* Pick a random population member */
144  /* Endless loop for NP < 4 !!! */
145  r3 = boost::uniform_int<int>(0,NP-1)(m_urng);
146  } while ((r3==i) || (r3==r1) || (r3==r2));
147 
148  do { /* Pick a random population member */
149  /* Endless loop for NP < 5 !!! */
150  r4 = boost::uniform_int<int>(0,NP-1)(m_urng);
151  } while ((r4==i) || (r4==r1) || (r4==r2) || (r4==r3));
152 
153  do { /* Pick a random population member */
154  /* Endless loop for NP < 6 !!! */
155  r5 = boost::uniform_int<int>(0,NP-1)(m_urng);
156  } while ((r5==i) || (r5==r1) || (r5==r2) || (r5==r3) || (r5==r4));
157 
158 
159  /*-------DE/best/1/exp--------------------------------------------------------------------*/
160  /*-------Our oldest strategy but still not bad. However, we have found several------------*/
161  /*-------optimization problems where misconvergence occurs.-------------------------------*/
162  if (m_strategy == 1) { /* strategy DE0 (not in our paper) */
163  tmp = popold[i];
164  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng), L = 0;
165  do {
166  tmp[n] = gbIter[n] + m_f*(popold[r2][n]-popold[r3][n]);
167  n = (n+1)%Dc;
168  ++L;
169  } while ((m_drng() < m_cr) && (L < Dc));
170  }
171 
172  /*-------DE/rand/1/exp-------------------------------------------------------------------*/
173  /*-------This is one of my favourite strategies. It works especially well when the-------*/
174  /*-------"gbIter[]"-schemes experience misconvergence. Try e.g. m_f=0.7 and m_cr=0.5---------*/
175  /*-------as a first guess.---------------------------------------------------------------*/
176  else if (m_strategy == 2) { /* strategy DE1 in the techreport */
177  tmp = popold[i];
178  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng), L = 0;
179  do {
180  tmp[n] = popold[r1][n] + m_f*(popold[r2][n]-popold[r3][n]);
181  n = (n+1)%Dc;
182  ++L;
183  } while ((m_drng() < m_cr) && (L < Dc));
184  }
185 
186  /*-------DE/rand-to-best/1/exp-----------------------------------------------------------*/
187  /*-------This strategy seems to be one of the best strategies. Try m_f=0.85 and m_cr=1.------*/
188  /*-------If you get misconvergence try to increase NP. If this doesn't help you----------*/
189  /*-------should play around with all three control variables.----------------------------*/
190  else if (m_strategy == 3) { /* similiar to DE2 but generally better */
191  tmp = popold[i];
192  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng), L = 0;
193  do {
194  tmp[n] = tmp[n] + m_f*(gbIter[n] - tmp[n]) + m_f*(popold[r1][n]-popold[r2][n]);
195  n = (n+1)%Dc;
196  ++L;
197  } while ((m_drng() < m_cr) && (L < Dc));
198  }
199  /*-------DE/best/2/exp is another powerful strategy worth trying--------------------------*/
200  else if (m_strategy == 4) {
201  tmp = popold[i];
202  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng), L = 0;
203  do {
204  tmp[n] = gbIter[n] +
205  (popold[r1][n]+popold[r2][n]-popold[r3][n]-popold[r4][n])*m_f;
206  n = (n+1)%Dc;
207  ++L;
208  } while ((m_drng() < m_cr) && (L < Dc));
209  }
210  /*-------DE/rand/2/exp seems to be a robust optimizer for many functions-------------------*/
211  else if (m_strategy == 5) {
212  tmp = popold[i];
213  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng), L = 0;
214  do {
215  tmp[n] = popold[r5][n] +
216  (popold[r1][n]+popold[r2][n]-popold[r3][n]-popold[r4][n])*m_f;
217  n = (n+1)%Dc;
218  ++L;
219  } while ((m_drng() < m_cr) && (L < Dc));
220  }
221 
222  /*=======Essentially same strategies but BINOMIAL CROSSOVER===============================*/
223 
224  /*-------DE/best/1/bin--------------------------------------------------------------------*/
225  else if (m_strategy == 6) {
226  tmp = popold[i];
227  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng);
228  for (size_t L = 0; L < Dc; ++L) { /* perform Dc binomial trials */
229  if ((m_drng() < m_cr) || L + 1 == Dc) { /* change at least one parameter */
230  tmp[n] = gbIter[n] + m_f*(popold[r2][n]-popold[r3][n]);
231  }
232  n = (n+1)%Dc;
233  }
234  }
235  /*-------DE/rand/1/bin-------------------------------------------------------------------*/
236  else if (m_strategy == 7) {
237  tmp = popold[i];
238  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng);
239  for (size_t L = 0; L < Dc; ++L) { /* perform Dc binomial trials */
240  if ((m_drng() < m_cr) || L + 1 == Dc) { /* change at least one parameter */
241  tmp[n] = popold[r1][n] + m_f*(popold[r2][n]-popold[r3][n]);
242  }
243  n = (n+1)%Dc;
244  }
245  }
246  /*-------DE/rand-to-best/1/bin-----------------------------------------------------------*/
247  else if (m_strategy == 8) {
248  tmp = popold[i];
249  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng);
250  for (size_t L = 0; L < Dc; ++L) { /* perform Dc binomial trials */
251  if ((m_drng() < m_cr) || L + 1 == Dc) { /* change at least one parameter */
252  tmp[n] = tmp[n] + m_f*(gbIter[n] - tmp[n]) + m_f*(popold[r1][n]-popold[r2][n]);
253  }
254  n = (n+1)%Dc;
255  }
256  }
257  /*-------DE/best/2/bin--------------------------------------------------------------------*/
258  else if (m_strategy == 9) {
259  tmp = popold[i];
260  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng);
261  for (size_t L = 0; L < Dc; ++L) { /* perform Dc binomial trials */
262  if ((m_drng() < m_cr) || L + 1 == Dc) { /* change at least one parameter */
263  tmp[n] = gbIter[n] +
264  (popold[r1][n]+popold[r2][n]-popold[r3][n]-popold[r4][n])*m_f;
265  }
266  n = (n+1)%Dc;
267  }
268  }
269  /*-------DE/rand/2/bin--------------------------------------------------------------------*/
270  else if (m_strategy == 10) {
271  tmp = popold[i];
272  size_t n = boost::uniform_int<int>(0,Dc-1)(m_urng);
273  for (size_t L = 0; L < Dc; ++L) { /* perform Dc binomial trials */
274  if ((m_drng() < m_cr) || L + 1 == Dc) { /* change at least one parameter */
275  tmp[n] = popold[r5][n] +
276  (popold[r1][n]+popold[r2][n]-popold[r3][n]-popold[r4][n])*m_f;
277  }
278  n = (n+1)%Dc;
279  }
280  }
281 
282 
283  /*=======Trial mutation now in tmp[]. force feasibility and how good this choice really was.==================*/
284  // a) feasibility
285  size_t i2 = 0;
286  while (i2<Dc) {
287  if ((tmp[i2] < lb[i2]) || (tmp[i2] > ub[i2]))
288  tmp[i2] = boost::uniform_real<double>(lb[i2],ub[i2])(m_drng);
289  ++i2;
290  }
291 
292  //b) how good?
293  prob.objfun(newfitness, tmp); /* Evaluate new vector in tmp[] */
294  if ( pop.problem().compare_fitness(newfitness,fit[i]) ) { /* improved objective function value ? */
295  fit[i]=newfitness;
296  popnew[i] = tmp;
297  // As a fitness improvment occured we move the point
298  // and thus can evaluate a new velocity
299  std::transform(tmp.begin(), tmp.end(), pop.get_individual(i).cur_x.begin(), tmp.begin(),std::minus<double>());
300  //updates x and v (cache avoids to recompute the objective function)
301  pop.set_x(i,popnew[i]);
302  pop.set_v(i,tmp);
303  if ( pop.problem().compare_fitness(newfitness,gbfit) ) {
304  /* if so...*/
305  gbfit=newfitness; /* reset gbfit to new low...*/
306  gbX=popnew[i];
307  }
308  } else {
309  popnew[i] = popold[i];
310  }
311 
312  }//End of the loop through the deme
313 
314  /* Save best population member of current iteration */
315  gbIter = gbX;
316 
317  /* swap population arrays. New generation becomes old one */
318  std::swap(popold, popnew);
319 
320 
321  //9 - Check the exit conditions (every 40 generations)
322  if (gen % 40 == 0) {
323  double dx = 0;
324  for (decision_vector::size_type i = 0; i < D; ++i) {
325  tmp[i] = pop.get_individual(pop.get_worst_idx()).best_x[i] - pop.get_individual(pop.get_best_idx()).best_x[i];
326  dx += std::fabs(tmp[i]);
327  }
328 
329  if ( dx < m_xtol ) {
330  if (m_screen_output) {
331  std::cout << "Exit condition -- xtol < " << m_xtol << std::endl;
332  }
333  return;
334  }
335 
336  double mah = std::fabs(pop.get_individual(pop.get_worst_idx()).best_f[0] - pop.get_individual(pop.get_best_idx()).best_f[0]);
337 
338  if (mah < m_ftol) {
339  if (m_screen_output) {
340  std::cout << "Exit condition -- ftol < " << m_ftol << std::endl;
341  }
342  return;
343  }
344 
345  // outputs current values
346  if (m_screen_output) {
347  std::cout << "Generation " << gen << " ***" << std::endl;
348  std::cout << " Best global fitness: " << pop.champion().f << std::endl;
349  std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl;
350  std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl;
351  }
352 
353  }
354 
355 
356  }//end main DE iterations
357  if (m_screen_output) {
358  std::cout << "Exit condition -- generations > " << m_gen << std::endl;
359  }
360 
361 }
362 
364 std::string de::get_name() const
365 {
366  return "Differential Evolution";
367 }
368 
370 
375 void de::set_cr(double cr) {
376  if (cr < 0 || cr > 1 ) {
377  pagmo_throw(value_error,"the cr parameter must be in the [0,1] range");
378  }
379  m_cr = cr;
380 }
381 
383 
388 void de::set_f(double f) {
389  if (f < 0 || f > 1 ) {
390  pagmo_throw(value_error,"the cr parameter must be in the [0,1] range");
391  }
392  m_f = f;
393 }
394 
396 
400 double de::get_cr() const {
401  return m_cr;
402 }
403 
404 
406 
410 double de::get_f() const {
411  return m_f;
412 }
413 
414 
416 
419 std::string de::human_readable_extra() const
420 {
421  std::ostringstream s;
422  s << "gen:" << m_gen << ' ';
423  s << "F: " << m_f << ' ';
424  s << "CR: " << m_cr << ' ';
425  s << "variant:" << m_strategy << ' ';
426  s << "ftol:" << m_ftol << ' ';
427  s << "xtol:" << m_xtol;
428  return s.str();
429 }
430 
431 }} //namespaces
432 
433 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::de)
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
void evolve(population &) const
Evolve implementation.
Definition: de.cpp:76
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
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
Base algorithm class.
void set_f(double cr)
Sets f parameter.
Definition: de.cpp:388
Base problem class.
Definition: problem/base.h:148
de(int=100, double=0.8, double=0.9, int=2, double=1e-6, double=1e-6)
Constructor.
Definition: de.cpp:50
void set_cr(double cr)
Sets crossover parameter.
Definition: de.cpp:375
Population class.
Definition: population.h:70
base_ptr clone() const
Clone method.
Definition: de.cpp:63
size_type get_dimension() const
Return global dimension.
decision_vector x
Decision vector.
Definition: population.h:149
bool compare_fitness(const fitness_vector &, const fitness_vector &) const
Compare fitness vectors.
double get_f() const
Gets f parameter.
Definition: de.cpp:410
fitness_vector f
Fitness vector.
Definition: population.h:153
Differential Evolution Algorithm.
Definition: de.h:70
fitness_vector objfun(const decision_vector &) const
Return fitness of pagmo::decision_vector.
bool m_screen_output
Indicates to the derived class whether to print stuff on screen.
size_type get_i_dimension() const
Return integer dimension.
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
double get_cr() const
Gets crossover parameter.
Definition: de.cpp:400
c_size_type get_c_dimension() const
Return global constraints dimension.
const decision_vector & get_ub() const
Upper bounds getter.
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.
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: de.cpp:419
const decision_vector & get_lb() const
Lower bounds getter.
std::string get_name() const
Algorithm name.
Definition: de.cpp:364
rng_double m_drng
Random number generator for double-precision floating point values.
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160