PaGMO  1.1.5
cstrs_immune_system.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 <string>
30 #include <vector>
31 
32 #include "../exceptions.h"
33 #include "../population.h"
34 #include "../problem/base.h"
35 #include "../problem/antibodies_problem.h"
36 #include "../problem/con2uncon.h"
37 #include "../types.h"
38 #include "base.h"
39 #include "cstrs_immune_system.h"
40 
41 namespace pagmo { namespace algorithm {
42 
44 
62 cstrs_immune_system::cstrs_immune_system(const base &original_algo, const base &original_algo_immune, int gen,
63  select_method_type select_method,
64  inject_method_type inject_method,
65  distance_method_type distance_method,
66  double phi,
67  double gamma,
68  double sigma,
69  double ftol, double xtol):
70  base(),m_gen(gen),m_select_method(select_method),m_inject_method(inject_method),m_distance_method(distance_method),
71  m_phi(phi),m_gamma(gamma),m_sigma(sigma),m_ftol(ftol),m_xtol(xtol)
72 {
73  m_original_algo = original_algo.clone();
74  m_original_algo_immune = original_algo_immune.clone();
75 
76  if(gen < 0) {
77  pagmo_throw(value_error,"number of generations must be nonnegative");
78  }
79 }
80 
83  base(algo),m_original_algo(algo.m_original_algo->clone()),
84  m_original_algo_immune(algo.m_original_algo_immune->clone()),m_gen(algo.m_gen),
85  m_select_method(algo.m_select_method),m_inject_method(algo.m_inject_method),m_distance_method(algo.m_distance_method),
86  m_phi(algo.m_phi),m_gamma(algo.m_gamma),m_sigma(algo.m_sigma),m_ftol(algo.m_ftol), m_xtol(algo.m_xtol)
87 {}
88 
91 {
92  return base_ptr(new cstrs_immune_system(*this));
93 }
94 
96 
102 {
103  // store useful variables
104  const problem::base &prob = pop.problem();
105  const population::size_type pop_size = pop.size();
106  const problem::base::size_type prob_dimension = prob.get_dimension();
107 
108  // get the constraints dimension
109  problem::base::c_size_type prob_c_dimension = prob.get_c_dimension();
110 
111  //We perform some checks to determine wether the problem/population are suitable for co-evolution
112  if(prob_c_dimension < 1) {
113  pagmo_throw(value_error,"The problem is not constrained and co-evolution is not suitable to solve it");
114  }
115  if(prob.get_f_dimension() != 1) {
116  pagmo_throw(value_error,"The problem is multiobjective and co-evolution is not suitable to solve it");
117  }
118 
119  // Get out if there is nothing to do.
120  if(pop_size == 0) {
121  return;
122  }
123 
124  // generates the unconstrained problem
125  problem::con2uncon prob_unconstrained(prob);
126 
127  // associates the population to this problem
128  population pop_mixed(prob_unconstrained);
129  std::vector<decision_vector> pop_mixed_c(pop_size);
130 
131  // initializaton of antigens vector and antibodies population
132 
133  // antigens population
134  std::vector<decision_vector> pop_antigens;
135 
136  // vector containing the best antigens position
137  std::vector<population::size_type> pop_antigens_pool;
138  std::vector<population::size_type> pop_antibodies_pool;
139 
140  pop_mixed.clear();
141  // the initial popluation contains the initial random population
142  for(population::size_type i=0; i<pop_size; i++) {
143  pop_mixed.push_back(pop.get_individual(i).cur_x);
144  }
145 
146  // Main Co-Evolution loop
147  for(int k=0; k<m_gen; k++) {
148 
149  pop_antigens.clear();
150 
151  // clearing the pools
152  pop_antigens_pool.clear();
153  pop_antibodies_pool.clear();
154 
155  // first of all we compute the constraints
156  for(population::size_type i=0; i<pop_size; i++) {
157  const population::individual_type &current_individual = pop_mixed.get_individual(i);
158  pop_mixed_c[i] = prob.compute_constraints(current_individual.cur_x);
159  }
160 
161  // we find if there are feasible individuals
162  bool has_feasible = false;
163 
164  for(population::size_type i=0; i<pop_size; i++) {
165  if(prob.feasibility_c(pop_mixed_c[i])) {
166  has_feasible = true;
167  break;
168  }
169  }
170 
171  // if feasible solutions exist in the population, the
172  // antigens are selected by being close to the average fitness
173  // of a sub population
174 
175  if(has_feasible) {
176  // the pop_antigens_pool is based on the feasible population
177  for(population::size_type i=0; i<pop_size; i++) {
178  if(prob.feasibility_c(pop_mixed_c[i])) {
179  pop_antigens_pool.push_back(i);
180  } else {
181  pop_antibodies_pool.push_back(i);
182  }
183  }
184 
185  population::size_type pop_antigens_pool_size = pop_antigens_pool.size();
186 
187  // we sort the pop_antigens_pool according to the fitness
188  for(population::size_type i=0; i<pop_antigens_pool_size-1; i++) {
189  const population::size_type &current_idx = pop_antigens_pool.at(i);
190  const population::individual_type &current_individual = pop_mixed.get_individual(current_idx);
191 
192  for(population::size_type j=i+1; j<pop_antigens_pool_size; j++) {
193  const population::size_type &current_second_idx = pop_antigens_pool.at(j);
194  const population::individual_type &current_second_individual = pop_mixed.get_individual(current_second_idx);
195 
196  if(prob.compare_fitness(current_second_individual.cur_f, current_individual.cur_f)) {
197  std::swap(pop_antigens_pool[i],pop_antigens_pool[j]);
198  }
199  }
200  }
201 
202  // a subset of the best antigens in the pool is selected to compute the fitness average
203  population::size_type pop_antigens_subset_size = std::max((population::size_type)(m_phi * pop_antigens_pool_size),(population::size_type)1);
204 
205  // we compute the mean fitness value from the subset of the best individuals in the population
206  double mean_fitness = 0.;
207  for(population::size_type i=0; i<pop_antigens_subset_size; i++) {
208  const population::size_type &current_idx = pop_antigens_pool.at(i);
209  mean_fitness += pop_mixed.get_individual(current_idx).cur_f.at(0);
210  }
211  mean_fitness /= pop_antigens_subset_size;
212 
213  // the population around this mean fitness is selected to fill the antigens population
214  // finds the position of the individual closest to the mean fitness
215 
216  // from the antigens pool we select a fraction from ones that are closest to the mean value
217  // according to Hjale and Lee. The idea here is to get as much information about the
218  // feasible domain as possible.
219 
220  int mean_position_idx=0;
221  for(population::size_type i=0; i<pop_antigens_pool_size; i++) {
222  const population::size_type &current_idx = pop_antigens_pool.at(i);
223  if(mean_fitness <= pop_mixed.get_individual(current_idx).cur_f.at(0)) {
224  mean_position_idx = i;
225  break;
226  }
227  }
228 
229  // generates the antigens population
230  int pop_antigens_size = std::max((int)(m_gamma * pop_antigens_pool_size), 1);
231 
232  population::size_type begin_antigen_idx = mean_position_idx - pop_antigens_size/2;
233  population::size_type end_antigen_idx = mean_position_idx + pop_antigens_size/2;
234 
235  // move the selection range depending on the boundaries
236  if(mean_position_idx - pop_antigens_size/2 < 0) {
237  begin_antigen_idx = 0;
238  end_antigen_idx = std::min((int)(end_antigen_idx + (pop_antigens_size/2 - mean_position_idx)),(int)pop_antigens_pool_size);
239  }
240  if(mean_position_idx + pop_antigens_size/2 >= pop_antigens_size) {
241  begin_antigen_idx = std::max((int)(begin_antigen_idx - (mean_position_idx + pop_antigens_size/2 - pop_antigens_size)),0);
242  end_antigen_idx = pop_antigens_size;
243  }
244 
245  // we select individuals around the mean
246  for(population::size_type i=begin_antigen_idx; i<end_antigen_idx; i++) {
247  population::size_type current_individual_idx = pop_antigens_pool.at(i);
248  pop_antigens.push_back(pop_mixed.get_individual(current_individual_idx).cur_x);
249  }
250 
251  if(pop_antigens.size() == 0) {
252  pop_antigens.push_back(pop_mixed.get_individual(pop_antigens_pool.at(mean_position_idx)).cur_x);
253  }
254 
255  } else {
256  // no feasible founds, the antigen population
257  // is selected accordingly to selected method
258 
259  switch(m_select_method) {
260  case(BEST_ANTIBODY): {
261  // Coello method the antigen population contains only one antigen which is the
262  // individual with lowest constraints violation
263  population::size_type best_idx = 0;
264  for(population::size_type i=1; i<pop_size; i++) {
265  if(prob.compare_constraints(pop_mixed_c[i], pop_mixed_c[best_idx])) {
266  best_idx = i;
267  }
268  }
269  pop_antigens_pool.push_back(best_idx);
270  pop_antigens.push_back(pop_mixed.get_individual(best_idx).cur_x);
271 
272  // antibodies
273  for(population::size_type i=0; i<pop_size; i++) {
274  if(i != best_idx){
275  pop_antibodies_pool.push_back(i);
276  }
277  }
278  break;
279  }
280  case(INFEASIBILITY): {
281  // Personal method where the infeasibility is used
282  // to select the antigen population
283  for(population::size_type i=0; i<pop_size; i++) {
284  pop_antigens_pool.push_back(i);
285  }
286 
287  population::size_type pop_antigens_pool_size = pop_antigens_pool.size();
288 
289  // we sort the pop_antigens_pool according to the fitness
290  for(population::size_type i=0; i<pop_antigens_pool_size-1; i++) {
291  const population::size_type &current_idx = pop_antigens_pool.at(i);
292 
293  for(population::size_type j=i+1; j<pop_antigens_pool_size; j++) {
294  const population::size_type &current_second_idx = pop_antigens_pool.at(j);
295 
296  if(prob.compare_constraints(pop_mixed_c[current_second_idx], pop_mixed_c[current_idx])) {
297  std::swap(pop_antigens_pool[i],pop_antigens_pool[j]);
298  }
299  }
300  }
301 
302  // fill the antigens with the best ones
303  population::size_type pop_antigens_size = std::max((population::size_type)(m_gamma * pop_antigens_pool_size), (population::size_type)1);
304 
305  for(population::size_type i=0; i<pop_antigens_size; i++) {
306  population::size_type current_individual_idx = pop_antigens_pool.at(i);
307  pop_antigens.push_back(pop_mixed.get_individual(current_individual_idx).cur_x);
308  }
309 
310  // antibodies
311  // not the best implementation, trying to find a better one...
312  for(population::size_type i=0; i<pop_size; i++) {
313  if(std::find(pop_antigens_pool.begin(), pop_antigens_pool.begin()+pop_antigens_size, i) == pop_antigens_pool.begin()+pop_antigens_size) {
314  pop_antibodies_pool.push_back(i);
315  }
316  }
317  break;
318  }
319  default: {
320  pagmo_throw(value_error,"The antibody selection method must be either BEST_ANTIBODY or INFEASIBILITY.");
321  break;
322  }
323  }
324  }
325 
326  population::size_type initial_pop_antibodies_pool_size = pop_antibodies_pool.size();
327 
328  if(initial_pop_antibodies_pool_size != 0) {
329 
330  // initial pool size
331  // random shuffle of the antibodies as the antibodies population
332  // are randomly selected without replacement from the antibodies pool
333  if(initial_pop_antibodies_pool_size > 1) {
334  for (population::size_type i=0; i<initial_pop_antibodies_pool_size; i++)
335  {
336  int j = boost::uniform_int<int>(0, initial_pop_antibodies_pool_size - 1)(m_urng);
337  std::swap(pop_antibodies_pool[i], pop_antibodies_pool[j]);
338  }
339  }
340 
341  // select the antibodies population with the requested size:
342  // antibodies population size = 1/3 antigens population size
343 
344  population::size_type pop_antigens_size = pop_antigens.size();
345 
346  population::size_type min_individual_for_algo = 8;
347 
348  population::size_type pop_antibodies_size = std::max( (int)(m_sigma * pop_antigens_size), (int)min_individual_for_algo);
349  pop_antibodies_size = std::min(pop_antibodies_size, initial_pop_antibodies_pool_size);
350 
351  //population::size_type pop_antibodies_size = std::max((int)(0.5 * pop_antigens_size), 6);
352  //population::size_type pop_antibodies_size = std::max((int)(pop_antibodies_pool.size()), 6);
353 
354  // the problem can be updated with antigenes, need to be done here to avoid a cast
355  problem::antibodies_problem prob_antibodies(prob, m_distance_method);
356  prob_antibodies.set_antigens(pop_antigens);
357 
358  // immune system initialization
359  population pop_antibodies(prob_antibodies);
360  pop_antibodies.clear();
361 
362  for(population::size_type i=0; i<pop_antibodies_size; i++) {
363  pop_antibodies.push_back(pop_mixed.get_individual(pop_antibodies_pool.at(i)).cur_x);
364  }
365 
366  // ensure that the antibodies population has at least 6 individuals for de, sga, 8 for jde...
367  if(min_individual_for_algo>pop_antibodies_size){
368  population::size_type extra_antibodies_size = min_individual_for_algo - pop_antibodies_size;
369 
370  // add extra needed by randomly selecting the individuals in the pool
371  for(population::size_type i=0; i<extra_antibodies_size; i++) {
372  if(initial_pop_antibodies_pool_size > 1) {
373  int j = boost::uniform_int<int>(0, initial_pop_antibodies_pool_size - 1)(m_urng);
374  pop_antibodies.push_back(pop_mixed.get_individual(pop_antibodies_pool.at(j)).cur_x);
375  } else {
376  pop_antibodies.push_back(pop_mixed.get_individual(pop_antibodies_pool.at(0)).cur_x);
377  }
378  }
379  }
380 
381  pop_antigens_size = pop_antigens.size();
382  pop_antibodies_size = pop_antibodies.size();
383 
384  // run the immune system
385  m_original_algo_immune->evolve(pop_antibodies);
386 
387  // sets the mixed population with all current best designs
388  // and the copies of constraint conditioned designs
389  switch(m_inject_method) {
390  case(CHAMPION): {
391  for(population::size_type i=0; i<initial_pop_antibodies_pool_size; i++) {
392  const population::size_type &current_idx = pop_antibodies_pool.at(i);
393  // multiple copies of the best antibody
394  pop_mixed.set_x(current_idx, pop_antibodies.champion().x);
395  }
396  break;
397  }
398  case(BEST25): {
399  std::vector<population::size_type> pop_best_25(pop_antibodies_size);
400  for(population::size_type i=0; i<pop_antibodies_size; i++) {
401  pop_best_25[i] = i;
402  }
403 
404  for(population::size_type i=0; i<pop_antibodies_size-1; i++) {
405  const population::size_type &current_idx = pop_best_25.at(i);
406  const population::individual_type &current_individual = pop_mixed.get_individual(current_idx);
407 
408  for(population::size_type j=i+1; j<pop_antibodies_size; j++) {
409  const population::size_type &current_second_idx = pop_best_25.at(j);
410  const population::individual_type &current_second_individual = pop_mixed.get_individual(current_second_idx);
411 
412  if(prob.compare_fitness(current_second_individual.cur_f, current_individual.cur_f)) {
413  std::swap(pop_best_25[i],pop_best_25[j]);
414  }
415  }
416  }
417 
418  int best25_size = pop_antibodies_size/4;
419 
420  // multiple copies of the 25% bests
421  for(population::size_type i=0; i<initial_pop_antibodies_pool_size; i++) {
422  const population::size_type &current_idx = pop_antibodies_pool.at(i);
423  pop_mixed.set_x(current_idx, pop_antibodies.get_individual(pop_best_25.at(i % best25_size)).cur_x);
424  }
425  break;
426  }
427  default: {
428  pagmo_throw(value_error,"The antibody injection method must be either CHAMPION or BEST25.");
429  break;
430  }
431  }
432  }
433 
434  // for individuals, evolve the mixed population
435  // which is an unconstrained problem
436  // only one iteration should be done...
437  m_original_algo->evolve(pop_mixed);
438 
439  // Check the exit conditions (every 40 generations, just as DE)
440  if(k % 40 == 0) {
441  decision_vector tmp(prob_dimension);
442 
443  double dx = 0;
444  for(decision_vector::size_type i=0; i<prob_dimension; i++) {
445  tmp[i] = pop_mixed.get_individual(pop_mixed.get_worst_idx()).best_x[i] - pop_mixed.get_individual(pop_mixed.get_best_idx()).best_x[i];
446  dx += std::fabs(tmp[i]);
447  }
448 
449  if(dx < m_xtol ) {
450  if (m_screen_output) {
451  std::cout << "Exit condition -- xtol < " << m_xtol << std::endl;
452  }
453  break;
454  }
455 
456  double mah = std::fabs(pop_mixed.get_individual(pop_mixed.get_worst_idx()).best_f[0] - pop_mixed.get_individual(pop_mixed.get_best_idx()).best_f[0]);
457 
458  if(mah < m_ftol) {
459  if(m_screen_output) {
460  std::cout << "Exit condition -- ftol < " << m_ftol << std::endl;
461  }
462  break;
463  }
464 
465  // outputs current values
466  if(m_screen_output) {
467  std::cout << "Generation " << k << " ***" << std::endl;
468  std::cout << " Best global fitness: " << pop.champion().f << std::endl;
469  std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl;
470  std::cout << " xtol: " << dx << ", ftol: " << mah << std::endl;
471  }
472  }
473  }
474 
475  // store the final population in the main population
476  pop.clear();
477  for(population::size_type i=0; i<pop_size; i++) {
478  pop.push_back(pop_mixed.get_individual(i).cur_x);
479  }
480 }
481 
483 std::string cstrs_immune_system::get_name() const
484 {
485  return m_original_algo->get_name() + "[Immune]";
486 }
487 
489 
493 {
494  return m_original_algo->clone();
495 }
496 
498 
505 {
506  m_original_algo_immune = algo.clone();
507 }
509 
514 {
515  return m_original_algo_immune->clone();
516 }
517 
519 
525 {
526  m_original_algo = algo.clone();
527 }
528 
530 
534 {
535  std::ostringstream s;
536  s << "algorithms: " << m_original_algo->get_name() << " - " << m_original_algo_immune->get_name() << " ";
537  s << "\n\tConstraints handled with immune system algorithm";
538  return s.str();
539 }
540 
541 }} //namespaces
542 
543 BOOST_CLASS_EXPORT_IMPLEMENT(pagmo::algorithm::cstrs_immune_system)
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
void set_algorithm_immune(const base &)
Set algorithm.
Immune system constraints handling meta-algorithm.
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.
bool feasibility_c(const constraint_vector &) const
Test feasibility of constraint vector.
Individuals stored in the population.
Definition: population.h:80
bool compare_constraints(const constraint_vector &, const constraint_vector &) const
Compare constraint vectors.
Base problem class.
Definition: problem/base.h:148
std::string get_name() const
Algorithm name.
Population class.
Definition: population.h:70
size_type get_dimension() const
Return global dimension.
Constrained to unconstrained meta-problem.
Definition: con2uncon.h:48
decision_vector x
Decision vector.
Definition: population.h:149
distance_method_type
Type of antibodies problem distance method.
virtual base_ptr clone() const =0
Clone method.
select_method_type
Type of immune system antibody selection method.
bool compare_fitness(const fitness_vector &, const fitness_vector &) const
Compare fitness vectors.
fitness_vector f
Fitness vector.
Definition: population.h:153
base_ptr get_algorithm_immune() const
Get a copy of the internal local algorithm.
std::string human_readable_extra() const
Extra human readable algorithm info.
bool m_screen_output
Indicates to the derived class whether to print stuff on screen.
void set_antigens(const std::vector< decision_vector > &)
Updates the antigens population used to compute the fitness.
base_ptr clone() const
Clone method.
constraint_vector compute_constraints(const decision_vector &) const
Compute constraints and return constraint vector.
base_ptr get_algorithm() const
Get a copy of the internal local algorithm.
c_size_type get_c_dimension() const
Return global constraints dimension.
container_type::size_type size_type
Population size type.
Definition: population.h:192
inject_method_type
Type of immune system antibody injection method.
decision_vector cur_x
Current decision vector.
Definition: population.h:83
rng_uint32 m_urng
Random number generator for unsigned integer values.
void evolve(population &) const
Evolve implementation.
f_size_type get_f_dimension() const
Return fitness dimension.
constraint_vector::size_type c_size_type
Constraints' size type: the same as pagmo::constraint_vector's size type.
Definition: problem/base.h:164
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160
void set_algorithm(const base &)
Set algorithm.
cstrs_immune_system(const base &=jde(1), const base &=sga(), int gen=1, select_method_type=BEST_ANTIBODY, inject_method_type=CHAMPION, distance_method_type=EUCLIDEAN, double=0.5, double=0.5, double=1./3., double=1e-15, double=1e-15)
Constructor.