PaGMO  1.1.5
mde_pbx.cpp
1 /*****************************************************************************
2 * Copyright (C) 2004-2009 The PaGMO development team, *
3 * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4 * http://apps.sourceforge.net/mediawiki/pagmo *
5 * http://apps.sourceforge.net/mediawiki/pagmo/index.php?title=Developers *
6 * http://apps.sourceforge.net/mediawiki/pagmo/index.php?title=Credits *
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/math/constants/constants.hpp>
26 #include <boost/random/uniform_int.hpp>
27 #include <boost/random/uniform_real.hpp>
28 #include <boost/random/normal_distribution.hpp>
29 #include <boost/random/cauchy_distribution.hpp>
30 #include <boost/random/variate_generator.hpp>
31 #include <string>
32 #include <vector>
33 #include <cmath>
34 #include <algorithm>
35 
36 #include "../exceptions.h"
37 #include "../population.h"
38 #include "../types.h"
39 #include "base.h"
40 #include "mde_pbx.h"
41 
42 namespace pagmo { namespace algorithm {
43 
45 
56 mde_pbx::mde_pbx(int gen, double qperc, double nexp, double ftol, double xtol):base(), m_gen(gen), m_fsuccess(), m_fm(0.5),
57  m_crsuccess(), m_crm(0.7), m_qperc(qperc), m_nexp(nexp), m_ftol(ftol), m_xtol(xtol) {
58  if (gen < 0) {
59  pagmo_throw(value_error,"number of generations must be nonnegative");
60  }
61  if (qperc < 0.0 || qperc > 1.0) {
62  pagmo_throw(value_error,"percentage of population to choose from must be between 0.0 and 1.0");
63  }
64  if (nexp == 0.0) {
65  pagmo_throw(value_error,"the exponent of the powermean must not be 0.0!");
66  }
67 }
68 
71 {
72  return base_ptr(new mde_pbx(*this));
73 }
74 
76 
82 void mde_pbx::evolve(population &pop) const
83 {
84  // Let's store some useful variables.
85  const problem::base &prob = pop.problem();
86  const problem::base::size_type D = prob.get_dimension();
87  const decision_vector &lb = prob.get_lb(), &ub = prob.get_ub();
88  const population::size_type NP = pop.size();
89  const population::size_type NP_Part = m_qperc * NP; // here we rely on an implicit cast
90 
91  //We perform some checks to determine wether the problem/population are suitable for DE
92  if ( D == 0 ) {
93  pagmo_throw(value_error,"Dimension of the problem shall not be zero!");
94  }
95 
96  if ( prob.get_c_dimension() != 0 ) {
97  pagmo_throw(value_error,"The problem is not box constrained and MDE_pBX is not suitable to solve it");
98  }
99 
100  if ( prob.get_f_dimension() != 1 ) {
101  pagmo_throw(value_error,"The problem is not single objective and MDE_pBX is not suitable to solve it");
102  }
103 
104  if (NP < 3) {
105  pagmo_throw(value_error,"for this algorithm, at least 3 individuals in the population are needed");
106  }
107 
108  if (NP_Part < 1) {
109  pagmo_throw(value_error,"You need a higher number of individuals in your population for sampling");
110  }
111 
112  // Get out if there is nothing to do.
113  if (m_gen == 0) {
114  return;
115  }
116  // Some vectors used during evolution are allocated here.
117  decision_vector dummy(D), tmp(D); //dummy is used for initialisation purposes, tmp to contain the mutated candidate
118  fitness_vector newfitness(1); //new fitness of the mutated candidate
119 
120  // reserve space for saving successful values for f and cr. This guarantees that no memory is allocated during
121  // the main loop
122  m_fsuccess.reserve(NP);
123  m_crsuccess.reserve(NP);
124 
125  // Initializing the random number generators
126  boost::uniform_real<double> uniform(0.0,1.0);
127  boost::variate_generator<boost::lagged_fibonacci607 &, boost::uniform_real<double> > r_dist(m_drng,uniform);
128 
129  boost::uniform_int<decision_vector::size_type> r_c_idx(0,D-1);
130  boost::variate_generator<boost::mt19937 &, boost::uniform_int<decision_vector::size_type> > c_idx(m_urng,r_c_idx);
131 
132  boost::uniform_int<population::size_type> r_p_idx(0,NP-1);
133  boost::variate_generator<boost::mt19937 &, boost::uniform_int<population::size_type> > p_idx(m_urng,r_p_idx);
134 
135  boost::normal_distribution<double> nd(0.0, 1.0);
136  boost::variate_generator<boost::lagged_fibonacci607 &, boost::normal_distribution<double> > gauss(m_drng,nd);
137 
138  // Declaring temporary variables used by the main-loop
140  population::size_type r1, r2, bestq_idx, bestp_idx, j_rand;
141  std::vector<population::size_type> a(NP-1,0);
142  double cri, fi; //, wcr, wf;
143 
144  // **** Main Loop of MDE-pBX ****
145  for (int gen = 0; gen < m_gen; ++gen) {
146 
147  // make a snapshot of the current population
148  // as we loop over individuals pop will contain the new generation while pop_old remains unchanged
149  pagmo::population pop_old(pop);
150 
151  // clear the sets of successful scale factors and crossover probabilities
152  m_fsuccess.clear();
153  m_crsuccess.clear();
154 
155  // adjust parameter p controlling the elite of individuals to choose from
156  // as we start counting with gen=0 and not with gen=1 we use (gen) instead of (gen - 1) here
157  p = ceil((NP / 2.0) * ( 1.0 - (double)(gen) / m_gen));
158 
159  // get the p-best individuals
160  std::vector<population::size_type> pbest = pop_old.get_best_idx(p);
161 
162  // loop through all individuals
163  for (pagmo::population::size_type i = 0; i < NP; ++i) {
164 
165  // Get q% random indices excluding i
166  // First we build the index list (for example i=5 -> a = [1,2,3,4,6,7,8, .... ]) containing NP-1 entries
167  for (pagmo::population::size_type k = 0; k < NP-1; ++k) {
168  (k<i) ? a[k] = k : a[k] = k+1;
169  }
170  // Then we swap the first q% of the indexes
171  for (pagmo::population::size_type k = 0; k < NP_Part; ++k) {
172  r1 = boost::uniform_int<int>(k,NP-2)(m_urng);
173  std::swap(a[k], a[r1]);
174  }
175 
176  // find index of individual from q% sample with best fitness
177  bestq_idx = a[0];
178  for (pagmo::population::size_type k = 1; k < NP_Part; ++k) {
179  if ( prob.compare_fitness(pop_old.get_individual(a[k]).cur_f, pop_old.get_individual(bestq_idx).cur_f) ) {
180  bestq_idx = a[k];
181  }
182  }
183 
184  // choose two random distinct pop members
185  do {
186  /* Endless loop for NP < 2 !!! */
187  r1 = p_idx();
188  } while ((r1==i) || (r1==bestq_idx));
189 
190  do {
191  /* Endless loop for NP < 3 !!! */
192  r2 = p_idx();
193  } while ((r2==i) || (r2==r1) || (r2==bestq_idx));
194 
195  bestp_idx = pbest[boost::uniform_int<int>(0,p-1)(m_urng)];
196 
197  // sample scale factors
198  //do {
199  cri = 0.1 * gauss()+m_crm;
200  //} while ((cri <= 0.0) || (cri >=1.0));
201 
202 // FIRST difference from the paper cri is not resampled, just trimmed
203  cri = std::min(1.0,std::max(0.0,cri));
204 
205 
206 // SECOND difference from paper, fi is half trimmed and half resampled
207  do {
208  fi = m_fm + 0.1 * tan(boost::math::constants::pi<double>() * ( r_dist() - 0.5 ));
209  fi = std::min(1.0,fi);
210  } while (fi <= 0.0); // || (fi >=1.0));
211 
212  // fix a random dimension index
213  j_rand = c_idx();
214 
215  // Mutation + Crossover
216  for (size_t j = 0; j < D; ++j) {
217  if (j == j_rand || r_dist() < cri) {
218  tmp[j] = pop_old.get_individual(i).cur_x[j] + fi * (pop_old.get_individual(bestq_idx).cur_x[j] - pop_old.get_individual(i).cur_x[j] + pop_old.get_individual(r1).cur_x[j] - pop_old.get_individual(r2).cur_x[j]);
219 // The paper does not speak about constraint enforcing ....
220  if ((tmp[j] < lb[j]) || (tmp[j] > ub[j])) { // enforce box-bounds
221  tmp[j] = lb[j]+ r_dist()*(ub[j]-lb[j]);
222  }
223  } else {
224  tmp[j] = pop_old.get_individual(bestp_idx).cur_x[j];
225  }
226  }
227 
228  /*=======Trial mutation now in tmp[] and feasible. Test how good this choice really was.==========*/
229 
230 
231  // b) Compare with the objective function
232  prob.objfun(newfitness, tmp); /* Evaluate new vector in tmp[] and records it fitness in newfitness */
233  if ( pop.problem().compare_fitness(newfitness,pop_old.get_individual(i).cur_f) ) { /* improved objective function value ? */
234  // As a fitness improvement occured we
235  pop.set_x(i,tmp);
236  // and thus can evaluate a new velocity
237  std::transform(tmp.begin(), tmp.end(), pop_old.get_individual(i).cur_x.begin(), tmp.begin(),std::minus<double>());
238  // updates v (cache avoids to recompute the objective function)
239  pop.set_v(i,tmp);
240  // pop_old.set_x(i,tmp); (un-comment for a steady-state version)
241  // remember the successful scale factors
242  m_crsuccess.push_back(cri);
243  m_fsuccess.push_back(fi);
244  }
245  } // end of one generation (loop over individuals)
246 
247  // Update Crossover and Mutation Probabilities
248  if (!m_crsuccess.empty()) {
249 // wcr = 0.9 + (0.001 * r_dist());
250 // m_crm = (wcr * m_crm) + (1.0 - wcr) * powermean(m_crsuccess, m_nexp);
251 // THIRD difference from the paper. Adaptation happen with normally distributed noise and fm has two different regimes
252  m_crm = (0.9+0.001*std::abs(gauss()))*m_crm + 0.1*(1+0.001*std::abs(gauss())) * powermean(m_crsuccess, m_nexp);
253 // wf = 0.8 + (0.01 * r_dist());
254 // m_fm = (wf * m_fm) + ((1.0 - wf) * powermean(m_fsuccess, m_nexp));
255  (m_fm < 0.85) ? m_fm = (0.9+0.01*std::abs(gauss()))*m_fm + 0.1*(1+0.01*std::abs(gauss())) * powermean(m_fsuccess, m_nexp) :
256  m_fm = (0.8+0.01*std::abs(gauss()))*m_fm + 0.1*(1+0.01*std::abs(gauss())) * powermean(m_fsuccess, m_nexp);
257 
258  }
259 
260  //Check the exit conditions (every 30 generations)
261  if (gen % 30 == 0) {
262  double dx = 0;
263 
264  for (decision_vector::size_type k = 0; k < D; ++k) {
265  tmp[k] = pop.get_individual(pop.get_worst_idx()).best_x[k] - pop.get_individual(pop.get_best_idx()).best_x[k];
266  dx += std::fabs(tmp[k]);
267  }
268 
269  if ( dx < m_xtol ) {
270  if (m_screen_output) {
271  std::cout << "Exit condition -- xtol < " << m_xtol << std::endl;
272  }
273  return;
274  }
275 
276  double mah = std::fabs(pop.get_individual(pop.get_worst_idx()).best_f[0] - pop.get_individual(pop.get_best_idx()).best_f[0]);
277 
278  if (mah < m_ftol) {
279  if (m_screen_output) {
280  std::cout << "Exit condition -- ftol < " << m_ftol << std::endl;
281  }
282  return;
283  }
284 
285  // outputs current values
286  if (m_screen_output) {
287  std::cout << "Generation " << gen << " ***" << std::endl;
288  std::cout << " Best global fitness: " << pop.champion().f << std::endl;
289  std::cout << " Fm: " << m_fm << ", Crm: " << m_crm << ", p: " << p << std::endl;
290  std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl;
291  }
292  }
293  } // End of Generation main iteration
294 
295  if (m_screen_output) {
296  std::cout << "Exit condition -- generations > " << m_gen << std::endl;
297  }
298 }
299 
301 std::string mde_pbx::get_name() const
302 {
303  return "MDE_pBX";
304 }
305 
307 double mde_pbx::powermean(std::vector<double> v, double exp) const
308 {
309  // correct implementation of the power mean
310  double sum = 0.0;
311  size_t vsize = v.size();
312 
313  if (vsize == 0) return 0;
314 
315  for (size_t i = 0; i < vsize; ++i) {
316  sum += std::pow(v[i], exp) ;
317  }
318  return std::pow((sum / vsize), (1.0 / exp));
319 }
320 
322 
325 std::string mde_pbx::human_readable_extra() const
326 {
327  std::ostringstream s;
328  s << "gen:" << m_gen << ' ';
329  s << "q percentage:" << m_qperc << ' ';
330  s << "power mean exponent:" << m_nexp << ' ';
331  s << "ftol:" << m_ftol << ' ';
332  s << "xtol:" << m_xtol;
333 
334  return s.str();
335 }
336 
337 }} //namespaces
338 
339 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::mde_pbx)
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
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: mde_pbx.cpp:325
Base algorithm class.
MDE_pBX - Differential Evolution variant.
Definition: mde_pbx.h:63
Base problem class.
Definition: problem/base.h:148
Population class.
Definition: population.h:70
double powermean(std::vector< double >, double) const
Computes the powermean of a set given as a vector.
Definition: mde_pbx.cpp:307
size_type get_dimension() const
Return global dimension.
void evolve(population &) const
Evolve implementation.
Definition: mde_pbx.cpp:82
bool compare_fitness(const fitness_vector &, const fitness_vector &) const
Compare fitness vectors.
fitness_vector f
Fitness vector.
Definition: population.h:153
fitness_vector objfun(const decision_vector &) const
Return fitness of pagmo::decision_vector.
base_ptr clone() const
Clone method.
Definition: mde_pbx.cpp:70
bool m_screen_output
Indicates to the derived class whether to print stuff on screen.
mde_pbx(int=100, double=0.15, double=1.5, double=1e-30, double=1e-30)
Constructor.
Definition: mde_pbx.cpp:56
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.
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.
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: mde_pbx.cpp:301