PaGMO  1.1.5
nsga2.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 
31 
32 #include <string>
33 #include <vector>
34 #include <algorithm>
35 
36 #include "../exceptions.h"
37 #include "../population.h"
38 #include "../problem/base.h"
39 #include "../types.h"
40 #include "base.h"
41 #include "nsga2.h"
42 
43 namespace pagmo { namespace algorithm {
44 
46 
56 nsga2::nsga2(int gen, double cr, double eta_c, double m, double eta_m):base(),m_gen(gen),m_cr(cr),m_eta_c(eta_c),m_m(m),m_eta_m(eta_m)
57 {
58  if (gen < 0) {
59  pagmo_throw(value_error,"number of generations must be nonnegative");
60  }
61  if (cr >= 1 || cr < 0) {
62  pagmo_throw(value_error,"crossover probability must be in the [0,1[ range");
63  }
64  if (m < 0 || m > 1) {
65  pagmo_throw(value_error,"mutation probability must be in the [0,1] range");
66  }
67  if (eta_c <1 || eta_c >= 100) {
68  pagmo_throw(value_error,"Distribution index for crossover must be in 1..100");
69  }
70  if (eta_m <1 || eta_m >= 100) {
71  pagmo_throw(value_error,"Distribution index for mutation must be in 1..100");
72  }
73 }
74 
77 {
78  return base_ptr(new nsga2(*this));
79 }
80 
82 {
83  if (pop.get_pareto_rank(idx1) < pop.get_pareto_rank(idx2)) return idx1;
84  if (pop.get_pareto_rank(idx1) > pop.get_pareto_rank(idx2)) return idx2;
85  if (pop.get_crowding_d(idx1) > pop.get_crowding_d(idx2)) return idx1;
86  if (pop.get_crowding_d(idx1) < pop.get_crowding_d(idx2)) return idx2;
87  return ((m_drng() > 0.5) ? idx1 : idx2);
88 }
89 
90 void nsga2::crossover(decision_vector& child1, decision_vector& child2, pagmo::population::size_type parent1_idx, pagmo::population::size_type parent2_idx,const pagmo::population& pop) const
91 {
92 
93  problem::base::size_type D = pop.problem().get_dimension();
94  problem::base::size_type Di = pop.problem().get_i_dimension();
95  problem::base::size_type Dc = D - Di;
96  const decision_vector &lb = pop.problem().get_lb(), &ub = pop.problem().get_ub();
97  const decision_vector& parent1 = pop.get_individual(parent1_idx).cur_x;
98  const decision_vector& parent2 = pop.get_individual(parent2_idx).cur_x;
99  double y1,y2,yl,yu, rand, beta, alpha, betaq, c1, c2;
100  child1 = parent1;
101  child2 = parent2;
102  int site1, site2;
103 
104  //This implements a Simulated Binary Crossover SBX
105  if (m_drng() <= m_cr) {
106  for (pagmo::problem::base::size_type i = 0; i < Dc; i++) {
107  if ( (m_drng() <= 0.5) && (std::fabs(parent1[i]-parent2[i]) ) > 1.0e-14) {
108  if (parent1[i] < parent2[i]) {
109  y1 = parent1[i];
110  y2 = parent2[i];
111  } else {
112  y1 = parent2[i];
113  y2 = parent1[i];
114  }
115  yl = lb[i];
116  yu = ub[i];
117  rand = m_drng();
118 
119  beta = 1.0 + (2.0*(y1-yl)/(y2-y1));
120  alpha = 2.0 - std::pow(beta,-(m_eta_c+1.0));
121  if (rand <= (1.0/alpha))
122  {
123  betaq = std::pow((rand*alpha),(1.0/(m_eta_c+1.0)));
124  } else {
125  betaq = std::pow((1.0/(2.0 - rand*alpha)),(1.0/(m_eta_c+1.0)));
126  }
127  c1 = 0.5*((y1+y2)-betaq*(y2-y1));
128 
129  beta = 1.0 + (2.0*(yu-y2)/(y2-y1));
130  alpha = 2.0 - std::pow(beta,-(m_eta_c+1.0));
131  if (rand <= (1.0/alpha))
132  {
133  betaq = std::pow((rand*alpha),(1.0/(m_eta_c+1.0)));
134  } else {
135  betaq = std::pow((1.0/(2.0 - rand*alpha)),(1.0/(m_eta_c+1.0)));
136  }
137  c2 = 0.5*((y1+y2)+betaq*(y2-y1));
138 
139  if (c1<lb[i]) c1=lb[i];
140  if (c2<lb[i]) c2=lb[i];
141  if (c1>ub[i]) c1=ub[i];
142  if (c2>ub[i]) c2=ub[i];
143  if (m_drng() <= 0.5) {
144  child1[i] = c1; child2[i] = c2;
145  } else {
146  child1[i] = c2; child2[i] = c1;
147  }
148  }
149  }
150 
151  }
152 
153  //This implements two point binary crossover
154  for (pagmo::problem::base::size_type i = Dc; i < D; i++) {
155  if (m_drng() <= m_cr) {
156  boost::uniform_int<int> in_dist(0,Di-1);
157  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > ra_num(m_urng,in_dist);
158  site1 = ra_num();
159  site2 = ra_num();
160  if (site1 > site2) std::swap(site1,site2);
161  for(int j=0; j<site1; j++)
162  {
163  child1[j] = parent1[j];
164  child2[j] = parent2[j];
165  }
166  for(int j=site1; j<site2; j++)
167  {
168  child1[j] = parent2[j];
169  child2[j] = parent1[j];
170  }
171  for(pagmo::problem::base::size_type j=site2; j<Di; j++)
172  {
173  child1[j] = parent1[j];
174  child2[j] = parent2[j];
175  }
176  }
177  else {
178  child1[i] = parent1[i];
179  child2[i] = parent2[i];
180  }
181  }
182 }
183 
184 void nsga2::mutate(decision_vector& child, const pagmo::population& pop) const
185 {
186 
187  problem::base::size_type D = pop.problem().get_dimension();
188  problem::base::size_type Di = pop.problem().get_i_dimension();
189  problem::base::size_type Dc = D - Di;
190  const decision_vector &lb = pop.problem().get_lb(), &ub = pop.problem().get_ub();
191  double rnd, delta1, delta2, mut_pow, deltaq;
192  double y, yl, yu, val, xy;
193  int gen_num;
194 
195  //This implements the real polinomial mutation of an individual
196  for (pagmo::problem::base::size_type j=0; j < Dc; ++j){
197  if (m_drng() <= m_m) {
198  y = child[j];
199  yl = lb[j];
200  yu = ub[j];
201  delta1 = (y-yl)/(yu-yl);
202  delta2 = (yu-y)/(yu-yl);
203  rnd = m_drng();
204  mut_pow = 1.0/(m_eta_m+1.0);
205  if (rnd <= 0.5)
206  {
207  xy = 1.0-delta1;
208  val = 2.0*rnd+(1.0-2.0*rnd)*(pow(xy,(m_eta_m+1.0)));
209  deltaq = pow(val,mut_pow) - 1.0;
210  }
211  else
212  {
213  xy = 1.0-delta2;
214  val = 2.0*(1.0-rnd)+2.0*(rnd-0.5)*(pow(xy,(m_eta_m+1.0)));
215  deltaq = 1.0 - (pow(val,mut_pow));
216  }
217  y = y + deltaq*(yu-yl);
218  if (y<yl) y = yl;
219  if (y>yu) y = yu;
220  child[j] = y;
221  }
222  }
223 
224  //This implements the integer mutation for an individual
225  for (pagmo::problem::base::size_type j=Dc; j < D; ++j){
226  if (m_drng() <= m_m) {
227  y = child[j];
228  yl = lb[j];
229  yu = ub[j];
230  boost::uniform_int<int> in_dist(yl,yu-1);
231  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > ra_num(m_urng,in_dist);
232  gen_num = ra_num();
233  if (gen_num >= y) gen_num = gen_num + 1;
234  child[j] = gen_num;
235  }
236  }
237 }
239 
245 void nsga2::evolve(population &pop) const
246 {
247  // Let's store some useful variables.
248  const problem::base &prob = pop.problem();
249  const problem::base::size_type D = prob.get_dimension();
250  const problem::base::size_type prob_c_dimension = prob.get_c_dimension();
251  const population::size_type NP = pop.size();
252 
253 
254  //We perform some checks to determine wether the problem/population are suitable for NSGA-II
255  if ( prob_c_dimension != 0 ) {
256  pagmo_throw(value_error, "The problem is not box constrained and NSGA-II is not suitable to solve it");
257  }
258 
259  if (NP < 5 || (NP % 4 != 0) ) {
260  pagmo_throw(value_error, "for NSGA-II at least 5 individuals in the population are needed and the population size must be a multiple of 4");
261  }
262 
263  if ( prob.get_f_dimension() < 2 ) {
264  pagmo_throw(value_error, "The problem is not multiobjective, try some other algorithm than NSGA-II");
265  }
266 
267  // Get out if there is nothing to do.
268  if (m_gen == 0) {
269  return;
270  }
271 
272  std::vector<population::size_type> best_idx(NP), shuffle1(NP),shuffle2(NP);
273  population::size_type parent1_idx, parent2_idx;
274  decision_vector child1(D), child2(D);
275 
276  for (pagmo::population::size_type i=0; i< NP; i++) shuffle1[i] = i;
277  for (pagmo::population::size_type i=0; i< NP; i++) shuffle2[i] = i;
278 
279  boost::uniform_int<int> pop_idx(0,NP-1);
280  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > p_idx(m_urng,pop_idx);
281 
282  // Main NSGA-II loop
283  for (int g = 0; g<m_gen; g++) {
284  //At each generation we make a copy of the population into popnew
285  // We compute the crowding distance and the pareto rank of pop
287  population popnew(pop);
288 
289  //We create some pseudo-random permutation of the poulation indexes
290  std::random_shuffle(shuffle1.begin(),shuffle1.end(),p_idx);
291  std::random_shuffle(shuffle2.begin(),shuffle2.end(),p_idx);
292 
293  //We then loop thorugh all individuals with increment 4 to select two pairs of parents that will
294  //each create 2 new offspring
295  for (pagmo::population::size_type i=0; i< NP; i+=4) {
296  // We create two offsprings using the shuffled list 1
297  parent1_idx = tournament_selection(shuffle1[i], shuffle1[i+1],pop);
298  parent2_idx = tournament_selection(shuffle1[i+2], shuffle1[i+3],pop);
299  crossover(child1, child2, parent1_idx,parent2_idx,pop);
300  mutate(child1,pop);
301  mutate(child2,pop);
302  popnew.push_back(child1);
303  popnew.push_back(child2);
304 
305  // We repeat with the shuffled list 2
306  parent1_idx = tournament_selection(shuffle2[i], shuffle2[i+1],pop);
307  parent2_idx = tournament_selection(shuffle2[i+2], shuffle2[i+3],pop);
308  crossover(child1, child2, parent1_idx,parent2_idx,pop);
309  mutate(child1,pop);
310  mutate(child2,pop);
311  popnew.push_back(child1);
312  popnew.push_back(child2);
313  } // popnew now contains 2NP individuals
314 
315  // This method returns the sorted N best individuals in the population according to the crowded comparison operator
316  // defined in population.cpp
317  best_idx = popnew.get_best_idx(NP);
318  // We completely cancel the population (NOTE: memory of all individuals and the notion of
319  // champion is thus destroyed)
320  pop.clear();
321  for (population::size_type i=0; i < NP; ++i) pop.push_back(popnew.get_individual(best_idx[i]).cur_x);
322  } // end of main SGA loop
323 }
324 
326 std::string nsga2::get_name() const
327 {
328  return "Nondominated Sorting Genetic Algorithm II (NSGA-II)";
329 }
330 
332 
335 std::string nsga2::human_readable_extra() const
336 {
337  std::ostringstream s;
338  s << "gen:" << m_gen << ' ';
339  s << "cr:" << m_cr << ' ';
340  s << "eta_c:" << m_eta_c << ' ';
341  s << "m:" << m_m << ' ';
342  s << "eta_m:" << m_eta_m << std::endl;
343 
344  return s.str();
345 }
346 
347 }} //namespaces
348 
349 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::nsga2)
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
nsga2(int gen=100, double cr=0.95, double eta_c=10, double m=0.01, double eta_m=50)
Constructor.
Definition: nsga2.cpp:56
Nondominated Sorting genetic algorithm II (NSGA-II)
Definition: nsga2.h:49
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
void update_pareto_information() const
Update Pareto Information.
Definition: population.cpp:384
Base algorithm class.
Base problem class.
Definition: problem/base.h:148
Population class.
Definition: population.h:70
size_type get_dimension() const
Return global dimension.
size_type get_i_dimension() const
Return integer dimension.
double get_crowding_d(const size_type &) const
Get Crowding Distance.
Definition: population.cpp:356
base_ptr clone() const
Clone method.
Definition: nsga2.cpp:76
c_size_type get_c_dimension() const
Return global constraints dimension.
void evolve(population &) const
Evolve implementation.
Definition: nsga2.cpp:245
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
size_type get_pareto_rank(const size_type &) const
Get Pareto rank.
Definition: population.cpp:334
std::string human_readable_extra() const
Extra human readable algorithm info.
Definition: nsga2.cpp:335
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: nsga2.cpp:326