PaGMO  1.1.5
sms_emoa.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 "../problem/base.h"
37 #include "../types.h"
38 #include "base.h"
39 #include "sms_emoa.h"
40 
41 namespace pagmo { namespace algorithm {
42 
44 
56 sms_emoa::sms_emoa(int gen, int sel_m, double cr, double eta_c, double m, double eta_m):base(),m_gen(gen),
57  m_sel_m(sel_m),m_cr(cr),m_eta_c(eta_c),m_m(m),m_eta_m(eta_m)
58 {
59  validate_parameters();
60 }
61 
63 
68 sms_emoa::sms_emoa(const sms_emoa &orig) : base(),m_gen(orig.m_gen),m_sel_m(orig.m_sel_m),
69  m_cr(orig.m_cr),m_eta_c(orig.m_eta_c),m_m(orig.m_m),m_eta_m(orig.m_eta_m)
70 {
71  if (orig.m_hv_algorithm) {
72  m_hv_algorithm = orig.m_hv_algorithm->clone();
73  }
74 }
75 
77 
90 sms_emoa::sms_emoa(pagmo::util::hv_algorithm::base_ptr hv_algorithm, int gen, int sel_m, double cr, double eta_c, double m, double eta_m):base(),
91  m_gen(gen),m_sel_m(sel_m), m_cr(cr),m_eta_c(eta_c),m_m(m),m_eta_m(eta_m)
92 {
93  m_hv_algorithm = hv_algorithm;
94  validate_parameters();
95 }
96 
98 
104 {
105  return m_hv_algorithm;
106 }
107 
110 {
111  return base_ptr(new sms_emoa(*this));
112 }
113 
114 void sms_emoa::validate_parameters()
115 {
116  if (m_gen < 0) {
117  pagmo_throw(value_error,"number of generations must be nonnegative");
118  }
119  if (m_sel_m < 1 || m_sel_m > 2) {
120  pagmo_throw(value_error,"selection method must be equal to 1 (least contributor) or 2 (domination count)");
121  }
122  if (m_cr >= 1 || m_cr < 0) {
123  pagmo_throw(value_error,"crossover probability must be in the [0,1] range");
124  }
125  if (m_m < 0 || m_m > 1) {
126  pagmo_throw(value_error,"mutation probability must be in the [0,1] range");
127  }
128  if (m_eta_c <1 || m_eta_c >= 100) {
129  pagmo_throw(value_error,"Distribution index for crossover must be in 1..100");
130  }
131  if (m_eta_m <1 || m_eta_m >= 100) {
132  pagmo_throw(value_error,"Distribution index for mutation must be in 1..100");
133  }
134 }
135 
136 void sms_emoa::crossover(decision_vector& child1, decision_vector& child2, pagmo::population::size_type parent1_idx, pagmo::population::size_type parent2_idx,const pagmo::population& pop) const
137 {
138 
139  problem::base::size_type D = pop.problem().get_dimension();
140  problem::base::size_type Di = pop.problem().get_i_dimension();
141  problem::base::size_type Dc = D - Di;
142  const decision_vector &lb = pop.problem().get_lb(), &ub = pop.problem().get_ub();
143  const decision_vector& parent1 = pop.get_individual(parent1_idx).cur_x;
144  const decision_vector& parent2 = pop.get_individual(parent2_idx).cur_x;
145  double y1,y2,yl,yu, rand, beta, alpha, betaq, c1, c2;
146  child1 = parent1;
147  child2 = parent2;
148  int site1, site2;
149 
150  //This implements a Simulated Binary Crossover SBX
151  if (m_drng() <= m_cr) {
152  for (pagmo::problem::base::size_type i = 0; i < Dc; i++) {
153  if ( (m_drng() <= 0.5) && (std::fabs(parent1[i]-parent2[i]) ) > 1.0e-14) {
154  if (parent1[i] < parent2[i]) {
155  y1 = parent1[i];
156  y2 = parent2[i];
157  } else {
158  y1 = parent2[i];
159  y2 = parent1[i];
160  }
161  yl = lb[i];
162  yu = ub[i];
163  rand = m_drng();
164 
165  beta = 1.0 + (2.0*(y1-yl)/(y2-y1));
166  alpha = 2.0 - std::pow(beta,-(m_eta_c+1.0));
167  if (rand <= (1.0/alpha))
168  {
169  betaq = std::pow((rand*alpha),(1.0/(m_eta_c+1.0)));
170  } else {
171  betaq = std::pow((1.0/(2.0 - rand*alpha)),(1.0/(m_eta_c+1.0)));
172  }
173  c1 = 0.5*((y1+y2)-betaq*(y2-y1));
174 
175  beta = 1.0 + (2.0*(yu-y2)/(y2-y1));
176  alpha = 2.0 - std::pow(beta,-(m_eta_c+1.0));
177  if (rand <= (1.0/alpha))
178  {
179  betaq = std::pow((rand*alpha),(1.0/(m_eta_c+1.0)));
180  } else {
181  betaq = std::pow((1.0/(2.0 - rand*alpha)),(1.0/(m_eta_c+1.0)));
182  }
183  c2 = 0.5*((y1+y2)+betaq*(y2-y1));
184 
185  if (c1<lb[i]) c1=lb[i];
186  if (c2<lb[i]) c2=lb[i];
187  if (c1>ub[i]) c1=ub[i];
188  if (c2>ub[i]) c2=ub[i];
189  if (m_drng() <= 0.5) {
190  child1[i] = c1; child2[i] = c2;
191  } else {
192  child1[i] = c2; child2[i] = c1;
193  }
194  }
195  }
196 
197  }
198 
199  //This implements two point binary crossover
200  for (pagmo::problem::base::size_type i = Dc; i < D; i++) {
201  if (m_drng() <= m_cr) {
202  boost::uniform_int<int> in_dist(0,Di-1);
203  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > ra_num(m_urng,in_dist);
204  site1 = ra_num();
205  site2 = ra_num();
206  if (site1 > site2) std::swap(site1,site2);
207  for(int j=0; j<site1; j++)
208  {
209  child1[j] = parent1[j];
210  child2[j] = parent2[j];
211  }
212  for(int j=site1; j<site2; j++)
213  {
214  child1[j] = parent2[j];
215  child2[j] = parent1[j];
216  }
217  for(pagmo::problem::base::size_type j=site2; j<Di; j++)
218  {
219  child1[j] = parent1[j];
220  child2[j] = parent2[j];
221  }
222  }
223  else {
224  child1[i] = parent1[i];
225  child2[i] = parent2[i];
226  }
227  }
228 }
229 
230 void sms_emoa::mutate(decision_vector& child, const pagmo::population& pop) const
231 {
232 
233  problem::base::size_type D = pop.problem().get_dimension();
234  problem::base::size_type Di = pop.problem().get_i_dimension();
235  problem::base::size_type Dc = D - Di;
236  const decision_vector &lb = pop.problem().get_lb(), &ub = pop.problem().get_ub();
237  double rnd, delta1, delta2, mut_pow, deltaq;
238  double y, yl, yu, val, xy;
239  int gen_num;
240 
241  //This implements the real polynomial mutation of an individual
242  for (pagmo::problem::base::size_type j=0; j < Dc; ++j){
243  if (m_drng() <= m_m) {
244  y = child[j];
245  yl = lb[j];
246  yu = ub[j];
247  delta1 = (y-yl)/(yu-yl);
248  delta2 = (yu-y)/(yu-yl);
249  rnd = m_drng();
250  mut_pow = 1.0/(m_eta_m+1.0);
251  if (rnd <= 0.5)
252  {
253  xy = 1.0-delta1;
254  val = 2.0*rnd+(1.0-2.0*rnd)*(pow(xy,(m_eta_m+1.0)));
255  deltaq = pow(val,mut_pow) - 1.0;
256  }
257  else
258  {
259  xy = 1.0-delta2;
260  val = 2.0*(1.0-rnd)+2.0*(rnd-0.5)*(pow(xy,(m_eta_m+1.0)));
261  deltaq = 1.0 - (pow(val,mut_pow));
262  }
263  y = y + deltaq*(yu-yl);
264  if (y<yl) y = yl;
265  if (y>yu) y = yu;
266  child[j] = y;
267  }
268  }
269 
270  //This implements the integer mutation for an individual
271  for (pagmo::problem::base::size_type j=Dc; j < D; ++j) {
272  if (m_drng() <= m_m) {
273  y = child[j];
274  yl = lb[j];
275  yu = ub[j];
276  boost::uniform_int<int> in_dist(yl,yu-1);
277  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > ra_num(m_urng,in_dist);
278  gen_num = ra_num();
279  if (gen_num >= y) gen_num = gen_num + 1;
280  child[j] = gen_num;
281  }
282  }
283 }
284 
285 // Find the index of the least contributing individual
286 population::size_type sms_emoa::evaluate_s_metric_selection(const population & pop) const
287 {
288 
289  std::vector< std::vector< population::size_type> > fronts = pop.compute_pareto_fronts();
290 
291  const std::vector< population::size_type> &last_front = fronts.back();
292 
293  if (last_front.size() == 1) {
294  return last_front[0];
295  }
296 
297  // if the chosen method is to always to pick the least contributor, or when working solely on the first front
298  if (m_sel_m == 1 || fronts.size() == 1) {
299  std::vector<fitness_vector> points;
300  points.resize(last_front.size());
301 
302  for (population::size_type idx = 0 ; idx < last_front.size() ; ++idx) {
303  points[idx] = fitness_vector(pop.get_individual(last_front[idx]).cur_f);
304  }
305 
306  pagmo::util::hypervolume hypvol(points);
307  fitness_vector r = hypvol.get_nadir_point(1.0);
308 
309  population::size_type least_idx;
310 
311  if (m_hv_algorithm) {
312  least_idx = hypvol.least_contributor(r, m_hv_algorithm);
313  } else {
314  least_idx = hypvol.least_contributor(r);
315  }
316 
317  return last_front[least_idx];
318  } else { // if m_sel_m == 2 && fronts.size() > 1
319  population::size_type max_dom_count = 0;
320  population::size_type individual_idx = 0;
321 
322  for (population::size_type idx = 0 ; idx < last_front.size() ; ++idx) {
323  population::size_type current_dom_count = pop.get_domination_count(last_front[idx]);
324  if (current_dom_count > max_dom_count) {
325  max_dom_count = current_dom_count;
326  individual_idx = idx;
327  }
328  }
329  return last_front[individual_idx];
330  }
331 }
332 
333 
335 
341 void sms_emoa::evolve(population &pop) const
342 {
343  // Let's store some useful variables.
344  const problem::base &prob = pop.problem();
345  const problem::base::size_type D = prob.get_dimension();
346  const problem::base::size_type prob_c_dimension = prob.get_c_dimension();
347  const population::size_type NP = pop.size();
348 
349  if ( prob_c_dimension != 0 ) {
350  pagmo_throw(value_error, "The problem is not box constrained and SMS-EMOA is not suitable to solve it");
351  }
352 
353  if ( prob.get_f_dimension() < 2 ) {
354  pagmo_throw(value_error, "The problem is not multiobjective, try some other algorithm than SMS-EMOA");
355  }
356 
357  // Get out if there is nothing to do.
358  if (m_gen == 0) {
359  return;
360  }
361 
362  population::size_type parent1_idx, parent2_idx;
363  decision_vector child1(D), child2(D);
364 
365  // Main SMS-EMOA loop
366  for (int g = 0; g < m_gen; g++) {
367  // select two different parent indices from the population
368  parent1_idx = m_urng() % NP;
369  parent2_idx = ((m_urng() % (NP-1)) + parent1_idx) % NP;
370 
371  crossover(child1, child2, parent1_idx, parent2_idx, pop);
372  ++m_fevals;
373  mutate(child1, pop);
374  pop.push_back(child1);
375  pop.erase(evaluate_s_metric_selection(pop));
376  }
377 }
378 
380 std::string sms_emoa::get_name() const
381 {
382  return "S-Metric Selection Evolutionary Multiobjective Optimisation Algorithm (SMS-EMOA)";
383 }
384 
386 
390 {
391  std::ostringstream s;
392  s << "gen:" << m_gen << ' ';
393  s << "cr:" << m_cr << ' ';
394  s << "eta_c:" << m_eta_c << ' ';
395  s << "m:" << m_m << ' ';
396  s << "eta_m:" << m_eta_m << ' ';
397  s << "hv_algorithm:";
398  if (m_hv_algorithm) {
399  s << m_hv_algorithm->get_name();
400  } else {
401  s << "Chosen dynamically";
402  }
403  s << std::endl;
404 
405  return s.str();
406 }
407 
408 }} //namespaces
409 
410 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::sms_emoa)
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
Root PaGMO namespace.
void evolve(population &) const
Evolve implementation.
Definition: sms_emoa.cpp:341
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
base_ptr clone() const
Clone method.
Definition: sms_emoa.cpp:109
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
Population class.
Definition: population.h:70
pagmo::util::hv_algorithm::base_ptr get_hv_algorithm() const
Get the hypervolume algorithm used for the computation.
Definition: sms_emoa.cpp:103
size_type get_dimension() const
Return global dimension.
std::string get_name() const
Algorithm name.
Definition: sms_emoa.cpp:380
S-metric selection evolutionary multiobjective optimisation algorithm (SMS-EMOA)
Definition: sms_emoa.h:46
unsigned int m_fevals
A counter for the number of function evaluations.
size_type get_i_dimension() const
Return integer dimension.
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
boost::shared_ptr< base > base_ptr
Base hypervolume algorithm class.
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.
sms_emoa(const sms_emoa &)
Copy constructor.
Definition: sms_emoa.cpp:68
f_size_type get_f_dimension() const
Return fitness dimension.
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: sms_emoa.cpp:389
const decision_vector & get_lb() const
Lower bounds getter.
rng_double m_drng
Random number generator for double-precision floating point values.
Hypervolume class.
Definition: hypervolume.h:49
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160