PaGMO  1.1.5
hv_greedy_r_policy.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 <algorithm>
26 #include <boost/numeric/conversion/cast.hpp>
27 #include <utility>
28 #include <vector>
29 #include <algorithm>
30 
31 #include "../population.h"
32 #include "base.h"
33 #include "base_r_policy.h"
34 #include "hv_greedy_r_policy.h"
35 #include "fair_r_policy.h"
36 
37 #include "../util/hypervolume.h"
38 
39 using namespace pagmo::util;
40 
41 namespace pagmo { namespace migration {
42 
44 
51 hv_greedy_r_policy::hv_greedy_r_policy(const double &rate, rate_type type, const double nadir_eps):base_r_policy(rate,type), m_nadir_eps(nadir_eps) {}
52 
54 {
55  return base_r_policy_ptr(new hv_greedy_r_policy(*this));
56 }
57 
58 // Selection implementation.
59 std::vector<std::pair<population::size_type,std::vector<population::individual_type>::size_type> >
60  hv_greedy_r_policy::select(const std::vector<population::individual_type> &immigrants, const population &dest) const
61 {
62  // Fall back to fair_r_policy when facing a single-objective problem.
63  if (dest.problem().get_f_dimension() == 1) {
64  return fair_r_policy(m_rate, m_type).select(immigrants, dest);
65  }
66 
67  std::vector<population::individual_type> filtered_immigrants;
68  filtered_immigrants.reserve(immigrants.size());
69 
70  // Keeps information on the original indexing of immigrants after we filter out the duplicates
71  std::vector<unsigned int> original_immigrant_indices;
72  original_immigrant_indices.reserve(immigrants.size());
73 
74  // Remove the duplicates from the set of immigrants
75  std::vector<population::individual_type>::iterator im_it = (const_cast<std::vector<population::individual_type> &>(immigrants)).begin();
76  unsigned int im_idx = 0;
77  for( ; im_it != immigrants.end() ; ++im_it) {
78  decision_vector im_x((*im_it).cur_x);
79 
80  bool equal = true;
81  for ( unsigned int idx = 0 ; idx < dest.size() ; ++idx ) {
82  decision_vector isl_x(dest.get_individual(idx).cur_x);
83  equal = true;
84  for (unsigned int d_idx = 0 ; d_idx < im_x.size() ; ++d_idx) {
85  if (im_x[d_idx] != isl_x[d_idx]) {
86  equal = false;
87  break;
88  }
89  }
90  if (equal) {
91  break;
92  }
93  }
94  if (!equal) {
95  filtered_immigrants.push_back(*im_it);
96  original_immigrant_indices.push_back(im_idx);
97  }
98  ++im_idx;
99  }
100 
101  // Computes the number of immigrants to be selected (accounting for the destination pop size)
102  const population::size_type rate_limit = std::min<population::size_type>(get_n_individuals(dest), boost::numeric_cast<population::size_type>(filtered_immigrants.size()));
103 
104  // Defines the retvalue
105  std::vector<std::pair<population::size_type, std::vector<population::individual_type>::size_type> > result;
106 
107  // Skip the remaining computation if there's nothing to do
108  if (rate_limit == 0) {
109  return result;
110  }
111 
112  // Makes a copy of the destination population
113  population pop_copy(dest);
114 
115  // Merge the immigrants to the copy of the destination population
116  for (population::size_type i = 0; i < rate_limit; ++i) {
117  pop_copy.push_back(filtered_immigrants[i].cur_x);
118  }
119 
120  // Population fronts stored as indices of individuals.
121  std::vector< std::vector<population::size_type> > fronts_i = pop_copy.compute_pareto_fronts();
122 
123  // Population fronts stored as fitness vectors of individuals.
124  std::vector< std::vector<fitness_vector> > fronts_f (fronts_i.size());
125 
126  // Nadir point is established manually later, first point is a first "safe" candidate.
127  fitness_vector refpoint(pop_copy.get_individual(0).cur_f);
128 
129  // Fill fronts_f with fitness vectors and establish the nadir point
130  for (unsigned int f_idx = 0 ; f_idx < fronts_i.size() ; ++f_idx) {
131  fronts_f[f_idx].resize(fronts_i[f_idx].size());
132  for (unsigned int p_idx = 0 ; p_idx < fronts_i[f_idx].size() ; ++p_idx) {
133  fronts_f[f_idx][p_idx] = fitness_vector(pop_copy.get_individual(fronts_i[f_idx][p_idx]).cur_f);
134 
135  // Update the nadir point manually for efficiency.
136  for (unsigned int d_idx = 0 ; d_idx < fronts_f[f_idx][p_idx].size() ; ++d_idx) {
137  refpoint[d_idx] = std::max(refpoint[d_idx], fronts_f[f_idx][p_idx][d_idx]);
138  }
139  }
140  }
141 
142  // Epsilon is added to nadir point
143  for (unsigned int d_idx = 0 ; d_idx < refpoint.size() ; ++d_idx) {
144  refpoint[d_idx] += m_nadir_eps;
145  }
146 
147  // Store which front we process (start with the last front) and the number of processed individuals.
148  unsigned int front_idx = fronts_i.size() - 1;
149  unsigned int processed_individuals = 0;
150 
151  // Vector for maintaining the original indices of points for given front
152  std::vector<unsigned int> orig_indices(fronts_i[front_idx].size());
153  iota(orig_indices.begin(), orig_indices.end(), 0);
154 
155  // Vector for maintaining the original indices of points for augmented population as 0 and 1
156  std::vector<unsigned int> g_orig_indices(pop_copy.size(), 1);
157 
158  unsigned int no_discarded_immigrants = 0;
159 
160  // Pairs of (islander index, islander exclusive hypervolume)
161  // Second item is updated later
162  std::vector<std::pair<unsigned int, double> > discarded_islanders;
163 
164  // Stops when we reduce the augmented population to the size of the original population or when the number of discarded islanders reaches the limit
165  while (processed_individuals < filtered_immigrants.size() && discarded_islanders.size() < rate_limit) {
166  // If current front is depleted, load next front.
167  if (fronts_f[front_idx].size() == 0) {
168  // Decrease front and reset the orig_indices
169  --front_idx;
170  orig_indices.resize(fronts_i[front_idx].size());
171  iota(orig_indices.begin(), orig_indices.end(), 0);
172  }
173 
174  // Initiate the hypervolume object and compute the least contributor
175  hypervolume hv(fronts_f[front_idx], false);
176  hv.set_copy_points(false);
177  unsigned int lc_idx = hv.least_contributor(refpoint);
178 
179  // Fix the index shift
180  unsigned int orig_lc_idx = fronts_i[front_idx][orig_indices[lc_idx]];
181 
182  if (orig_lc_idx < dest.size()) {
183  discarded_islanders.push_back(std::make_pair(orig_lc_idx, 0.0));
184  } else {
185  ++no_discarded_immigrants;
186  }
187 
188  // Flag given individual as discarded
189  g_orig_indices[orig_lc_idx] = 0;
190 
191  // Drop the local index, and the point from the front
192  orig_indices.erase(orig_indices.begin() + lc_idx);
193  fronts_f[front_idx].erase(fronts_f[front_idx].begin() + lc_idx);
194  ++processed_individuals;
195  }
196 
197  // Number of non-discarded immigrants
198  unsigned int no_available_immigrants = boost::numeric_cast<unsigned int>(filtered_immigrants.size() - no_discarded_immigrants);
199 
200  // Pairs of (immigrant index, immigrant exclusive hypervolume)
201  // Second item is updated later
202  std::vector<std::pair<unsigned int, double> > available_immigrants;
203  available_immigrants.reserve(no_available_immigrants);
204  for(unsigned int idx = dest.size() ; idx < pop_copy.size() ; ++idx) {
205  // If the immigrant was not discarded add it to the available set
206  if ( g_orig_indices[idx] == 1 ) {
207  available_immigrants.push_back(std::make_pair(idx, 0.0));
208  }
209  }
210 
211  // Aggregate all points to establish the hypervolume contribution of available immigrants and discarded islanders
212  std::vector<fitness_vector> merged_fronts;
213  merged_fronts.reserve(pop_copy.size());
214 
215  for(unsigned int idx = 0 ; idx < pop_copy.size() ; ++idx) {
216  merged_fronts.push_back(pop_copy.get_individual(idx).cur_f);
217  }
218 
219  hypervolume hv(merged_fronts, false);
220  std::vector<std::pair<unsigned int, double> >::iterator it;
221 
222  for(it = available_immigrants.begin() ; it != available_immigrants.end() ; ++it) {
223  (*it).second = hv.exclusive((*it).first, refpoint);
224  }
225 
226  for(it = discarded_islanders.begin() ; it != discarded_islanders.end() ; ++it) {
227  (*it).second = hv.exclusive((*it).first, refpoint);
228  }
229 
230  // Sort islanders and immigrants according to exclusive hypervolume
231  sort(available_immigrants.begin(), available_immigrants.end(), hv_greedy_r_policy::ind_cmp);
232  sort(discarded_islanders.begin(), discarded_islanders.end(), hv_greedy_r_policy::ind_cmp);
233 
234  // Number of exchanges is the minimum of the number of non discarded immigrants and the number of discarded islanders
235  unsigned int no_exchanges = std::min(boost::numeric_cast<unsigned int>(available_immigrants.size()), boost::numeric_cast<unsigned int>(discarded_islanders.size()));
236 
237  it = available_immigrants.begin();
238  std::vector<std::pair<unsigned int, double> >::reverse_iterator r_it = discarded_islanders.rbegin();
239 
240  // Match the best immigrant (forward iterator) with the worst islander (reverse iterator) no_exchanges times.
241  for(unsigned int i = 0 ; i < no_exchanges ; ++i) {
242  // Break if any islander is better than an immigrant
243  if ((*r_it).second > (*it).second) {
244  break;
245  }
246  // Push the pair (islander_idx, fixed_immigrant_idx) to the results
247  result.push_back(std::make_pair((*r_it).first, original_immigrant_indices[(*it).first - dest.size()]));
248  ++r_it;
249  ++it;
250  }
251 
252  return result;
253 }
254 
256 bool hv_greedy_r_policy::ind_cmp(const std::pair<unsigned int, double> &a, const std::pair<unsigned int, double> &b) {
257  return a.second > b.second;
258 }
259 
260 }}
261 
262 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::migration::hv_greedy_r_policy)
Root PaGMO namespace.
hv_greedy_r_policy(const double &rate=1, rate_type type=absolute, double nadir_eps=1.0)
Constructor from rate and rate type.
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
Base class for migration replacement policies.
Definition: base_r_policy.h:57
fitness_vector cur_f
Current fitness vector.
Definition: population.h:89
boost::shared_ptr< base_r_policy > base_r_policy_ptr
Shared pointer to base replacement policy.
Definition: base_r_policy.h:40
std::vector< std::pair< population::size_type, std::vector< population::individual_type >::size_type > > select(const std::vector< population::individual_type > &, const population &) const
Assign pairs of individuals for replacement during migration.
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
Population class.
Definition: population.h:70
Fair replacement policy.
Definition: fair_r_policy.h:49
base_r_policy_ptr clone() const
Clone method.
population::size_type get_n_individuals(const population &) const
Get number of individuals to migrate from/to input population.
unsigned int least_contributor(const fitness_vector &, const hv_algorithm::base_ptr) const
Find the least contributing individual.
std::vector< std::pair< population::size_type, std::vector< population::individual_type >::size_type > > select(const std::vector< population::individual_type > &, const population &) const
Assign pairs of individuals for replacement during migration.
std::vector< double > fitness_vector
Fitness vector type.
Definition: types.h:42
util namespace.
Definition: util/config.h:42
Replace the 'n' successive least contributors with the incoming set.
static void iota(ForwardIterator first, ForwardIterator last, T value)
Iota function, usefull to fill iterator range with increasing values.
container_type::size_type size_type
Population size type.
Definition: population.h:192
decision_vector cur_x
Current decision vector.
Definition: population.h:83
rate_type m_type
Migration rate type.
f_size_type get_f_dimension() const
Return fitness dimension.
Hypervolume class.
Definition: hypervolume.h:49
double m_rate
Migration rate.
double exclusive(const unsigned int, const fitness_vector &, const hv_algorithm::base_ptr) const
Compute exclusive contribution.
void set_copy_points(const bool)
Setter for 'copy_points' flag.
Definition: hypervolume.cpp:99
std::vector< std::vector< size_type > > compute_pareto_fronts() const
Computes and returns the population Pareto fronts.
Definition: population.cpp:475
rate_type
Type of migration rate.