PaGMO  1.1.5
base_island.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/date_time/posix_time/posix_time.hpp>
26 #include <boost/numeric/conversion/cast.hpp>
27 #include <boost/thread/thread.hpp>
28 #include <boost/random/uniform_int.hpp>
29 #include <boost/random/variate_generator.hpp>
30 #include <cstddef>
31 #include <exception>
32 #include <iostream>
33 #include <sstream>
34 #include <stdexcept>
35 #include <string>
36 #include <typeinfo>
37 #include <utility>
38 #include <vector>
39 
40 #include "algorithm/base.h"
41 #include "archipelago.h"
42 #include "base_island.h"
43 #include "exceptions.h"
44 #include "migration/base_r_policy.h"
45 #include "migration/base_s_policy.h"
46 #include "population.h"
47 #include "problem/base.h"
48 #include "types.h"
49 
50 namespace pagmo
51 {
53 
66  const migration::base_s_policy &s_policy, const migration::base_r_policy &r_policy):
67  m_algo(a.clone()),m_pop(p,n),m_archi(0),m_evo_time(0),m_s_policy(s_policy.clone()),m_r_policy(r_policy.clone()) { }
68 
70 
75 base_island::base_island(const base_island &isl):m_pop(isl.get_population())
76 {
77  // Population has already been done and get_population() above already called join().
78  m_algo = isl.m_algo->clone();
79  m_archi = isl.m_archi;
80  m_evo_time = isl.m_evo_time;
81  m_s_policy = isl.m_s_policy->clone();
82  m_r_policy = isl.m_r_policy->clone();
83  m_evo_thread.reset(0);
84 }
85 
87 
98  const migration::base_s_policy &s_policy, const migration::base_r_policy &r_policy):
99  m_algo(a.clone()),m_pop(pop),m_archi(0),m_evo_time(0),m_s_policy(s_policy.clone()),m_r_policy(r_policy.clone()) { }
100 
102 
110 {
111  if (this != &isl) {
112  // Make sure both islands are in a known state.
113  join();
114  isl.join();
115  // Copy over content.
116  m_algo = isl.m_algo->clone();
117  m_pop = isl.m_pop;
118  m_archi = isl.m_archi;
119  m_evo_time = isl.m_evo_time;
120  m_s_policy = isl.m_s_policy->clone();
121  m_r_policy = isl.m_r_policy->clone();
122  m_evo_thread.reset(0);
123  }
124  return *this;
125 }
126 
128 
132 {
134 }
135 
137 
142 std::string base_island::get_name() const
143 {
144  join();
145  return typeid(*this).name();
146 }
147 
149 
158 {
159  join();
160  std::ostringstream oss;
161  oss << "Island type: " << get_name() << "\n\n";
162  oss << *m_algo << "\n\n";
163  oss << "Evolution time: " << m_evo_time << " milliseconds\n\n";
164  oss << *m_s_policy << '\n';
165  oss << *m_r_policy << '\n';
166  oss << m_pop.human_readable_terse() << '\n';
167  return oss.str();
168 }
169 
171 
179 std::string base_island::human_readable() const
180 {
181  join();
182  std::ostringstream oss;
183  oss << "Island type: " << get_name() << "\n\n";
184  oss << *m_algo << "\n\n";
185  oss << "Evolution time: " << m_evo_time << " milliseconds\n\n";
186  oss << *m_s_policy << '\n';
187  oss << *m_r_policy << '\n';
188  oss << m_pop.human_readable();
189  return oss.str();
190 }
191 
193 
199 void base_island::join() const
200 {
201  if (m_evo_thread && m_evo_thread->joinable()) {
202  m_evo_thread->join();
203  }
204 }
205 
207 
212 {}
213 
215 
220 {}
221 
222 // RAII class to call thread hooks in base_island.
223 struct base_island::raii_thread_hook
224 {
225  raii_thread_hook(base_island *ptr):m_ptr(ptr)
226  {
227  m_ptr->thread_entry();
228  }
229  ~raii_thread_hook()
230  {
231  m_ptr->thread_exit();
232  }
233  base_island *m_ptr;
234 };
235 
236 // Evolver thread object. This is a callable helper object used to launch an evolution for a given number of iterations.
237 struct base_island::int_evolver {
238  int_evolver(base_island *i, const std::size_t &n):m_i(i),m_n(n) {}
239  void operator()();
240  void juice_impl(boost::posix_time::ptime &);
241  base_island *m_i;
242  const std::size_t m_n;
243 };
244 
245 void base_island::int_evolver::juice_impl(boost::posix_time::ptime &start)
246 {
247  start = boost::posix_time::microsec_clock::local_time();
248  // Synchronise start with all other threads if we are in an archi.
249  if (m_i->m_archi) {
250  m_i->m_archi->sync_island_start();
251  }
252  const raii_thread_hook hook(m_i);
253  for (std::size_t i = 0; i < m_n; ++i) {
254  // Call pre-evolve hooks.
255  if (m_i->m_archi) {
256  m_i->m_archi->pre_evolution(*m_i);
257  }
258  m_i->m_pop.problem().pre_evolution(m_i->m_pop);
259  // Call the evolution.
260  m_i->perform_evolution(*m_i->m_algo,m_i->m_pop);
261  // Post-evolve hooks.
262  if (m_i->m_archi) {
263  m_i->m_archi->post_evolution(*m_i);
264  }
265  m_i->m_pop.problem().post_evolution(m_i->m_pop);
266  // Set the interruption point.
267  boost::this_thread::interruption_point();
268  }
269 }
270 
271 // Implementation of int_evolver's juice.
272 void base_island::int_evolver::operator()()
273 {
274  boost::posix_time::ptime start;
275  try {
276  juice_impl(start);
277  } catch (const boost::thread_interrupted &) {
278  // In case of interruption, don't do anything special.
279  } catch (const std::exception &e) {
280  std::cout << "Error during island evolution using " << m_i->m_algo->get_name() << ": " << e.what() << std::endl;
281  } catch (...) {
282  std::cout << "Error during island evolution using " << m_i->m_algo->get_name() << ", unknown exception caught. :(" << std::endl;
283  }
284  // Try to compute the evolution time before exiting. In case something goes wrong, do not do anything.
285  try {
286  // We must take care of potentially low-accuracy clocks, where the time difference could be negative for
287  // _really_ short evolution times. In that case do not add anything to the total evolution time.
288  const boost::posix_time::time_duration diff = boost::posix_time::microsec_clock::local_time() - start;
289  if (diff.total_milliseconds() >= 0) {
290  m_i->m_evo_time += boost::numeric_cast<std::size_t>(diff.total_milliseconds());
291  }
292  } catch (...) {
293  std::cout << "Error calculating evolution time.\n";
294  }
295 }
296 
298 
309 {
310  join();
311  const std::size_t n_evo = boost::numeric_cast<std::size_t>(n);
312  try {
313  m_evo_thread.reset(new boost::thread(int_evolver(this,n_evo)));
314  } catch (...) {
315  pagmo_throw(std::runtime_error,"failed to launch the thread");
316  }
317 }
318 
319 // Time-dependent evolver thread object. This is a callable helper object used to launch an evolution for a specified amount of time.
320 struct base_island::t_evolver {
321  t_evolver(base_island *i, const std::size_t &t):m_i(i),m_t(t) {}
322  void operator()();
323  void juice_impl(boost::posix_time::ptime &);
324  base_island *m_i;
325  const std::size_t m_t;
326 };
327 
328 void base_island::t_evolver::juice_impl(boost::posix_time::ptime &start)
329 {
330  boost::posix_time::time_duration diff;
331  start = boost::posix_time::microsec_clock::local_time();
332  // Synchronise start.
333  if (m_i->m_archi) {
334  m_i->m_archi->sync_island_start();
335  }
336  const raii_thread_hook hook(m_i);
337  do {
338  if (m_i->m_archi) {
339  m_i->m_archi->pre_evolution(*m_i);
340  }
341  m_i->m_pop.problem().pre_evolution(m_i->m_pop);
342  m_i->perform_evolution(*m_i->m_algo,m_i->m_pop);
343  if (m_i->m_archi) {
344  m_i->m_archi->post_evolution(*m_i);
345  }
346  m_i->m_pop.problem().post_evolution(m_i->m_pop);
347  // Set the interruption point.
348  boost::this_thread::interruption_point();
349  diff = boost::posix_time::microsec_clock::local_time() - start;
350  // Take care of negative timings.
351  } while (diff.total_milliseconds() < 0 || boost::numeric_cast<std::size_t>(diff.total_milliseconds()) < m_t);
352 }
353 
354 // Perform at least one evolution, and continue evolving until at least a certain amount of time has passed.
355 void base_island::t_evolver::operator()()
356 {
357  boost::posix_time::ptime start;
358  try {
359  juice_impl(start);
360  } catch (const boost::thread_interrupted &) {
361  // In case of interruption, don't do anything special.
362  } catch (const std::exception &e) {
363  std::cout << "Error during island evolution using " << m_i->m_algo->get_name() << ": " << e.what() << std::endl;
364  } catch (...) {
365  std::cout << "Error during island evolution using " << m_i->m_algo->get_name() << ", unknown exception caught. :(" << std::endl;
366  }
367  // Try to compute the evolution time before exiting. In case something goes wrong, do not do anything.
368  try {
369  // We must take care of potentially low-accuracy clocks, where the time difference could be negative for
370  // _really_ short evolution times. In that case do not add anything to the total evolution time.
371  const boost::posix_time::time_duration diff = boost::posix_time::microsec_clock::local_time() - start;
372  if (diff.total_milliseconds() >= 0) {
373  m_i->m_evo_time += boost::numeric_cast<std::size_t>(diff.total_milliseconds());
374  }
375  } catch (...) {
376  std::cout << "Error calculating evolution time.\n";
377  }
378 }
379 
381 
392 {
393  join();
394  const std::size_t t_evo = boost::numeric_cast<std::size_t>(t);
395  try {
396  m_evo_thread.reset(new boost::thread(t_evolver(this,t_evo)));
397  } catch (...) {
398  pagmo_throw(std::runtime_error,"failed to launch the thread");
399  }
400 }
401 
403 
408 {
409  if (m_evo_thread) {
410  m_evo_thread->interrupt();
411  join();
412  }
413 }
414 
416 
419 bool base_island::busy() const
420 {
421  if (!m_evo_thread) {
422  return false;
423  }
424  return (!m_evo_thread->timed_join(boost::posix_time::milliseconds(1)));
425 }
426 
428 
435 {
436  join();
437  return m_evo_time;
438 }
439 
441 
445 {
446  join();
447  return m_algo->clone();
448 }
449 
451 
458 {
459  join();
460  m_pop.set_x(i,x);
461 }
462 
464 
471 {
472  join();
473  m_pop.set_v(i,v);
474 }
475 
477 
483 {
484  join();
485  m_algo = algo.clone();
486 }
487 
489 
493 {
494  join();
495  return m_pop.problem().clone();
496 }
497 
499 
503 {
504  join();
505  return m_pop.size();
506 }
507 
509 
513 {
514  join();
515  return m_s_policy->clone();
516 }
517 
519 
523 {
524  join();
525  return m_r_policy->clone();
526 }
527 
529 
533 {
534  join();
535  return m_pop;
536 }
537 
539 
543 {
544  join();
545  m_pop = pop;
546 }
547 
548 struct unary_predicate {
549  unary_predicate(std::pair<population::size_type, archipelago::size_type> pair) : m_pair(pair) {};
550  bool operator()(std::pair<population::size_type, archipelago::size_type> x){
551  return (m_pair.second == x.second);
552  }
553  std::pair<population::size_type, archipelago::size_type> m_pair;
554 
555 };
556 // Accept individuals incoming from a migration operation. Returns an std::vector containing the number of accepted individuals from each island
557 std::vector<std::pair<population::size_type, archipelago::size_type> > base_island::accept_immigrants(std::vector<std::pair<population::size_type, population::individual_type> > &immigrant_pairs)
558 {
559  std::vector<std::pair<population::size_type, archipelago::size_type> > retval;
560  // Make sure we are in an archipelago.
561  pagmo_assert(m_archi);
562  // We shuffle the immigrants as to make sure not to give preference to a particular island
563  boost::uniform_int<int> pop_idx(0,immigrant_pairs.size());
564  boost::variate_generator<boost::mt19937 &, boost::uniform_int<int> > p_idx(m_pop.m_urng,pop_idx);
565  std::random_shuffle(immigrant_pairs.begin(),immigrant_pairs.end(), p_idx);
566  // We extract the immigrants from the pair
567  std::vector<population::individual_type> immigrants;
568  immigrants.reserve(immigrant_pairs.size());
569  for (size_t i=0;i<immigrant_pairs.size();++i) {
570  immigrants.push_back(immigrant_pairs[i].second);
571  }
572 
573  std::vector<std::pair<population::size_type,std::vector<population::individual_type>::size_type> > rep;
574  rep = m_r_policy->select(immigrants,m_pop);
575  for (std::vector<std::pair<population::size_type,std::vector<population::individual_type>::size_type> >::const_iterator
576  rep_it = rep.begin(); rep_it != rep.end(); ++rep_it)
577  {
578  pagmo_assert((*rep_it).first < m_pop.m_container.size() && (*rep_it).second < immigrants.size());
579  m_pop.m_container[(*rep_it).first] = immigrants[(*rep_it).second];
580  m_pop.update_champion((*rep_it).first);
581  m_pop.update_dom((*rep_it).first);
582  std::pair<population::size_type, archipelago::size_type> pair = std::make_pair(1.0, immigrant_pairs[(*rep_it).second].first);
583  std::vector<std::pair<population::size_type, archipelago::size_type> >::iterator where;
584  where = std::find_if(retval.begin(), retval.end(), unary_predicate(pair));
585  if (where == retval.end()) {
586  retval.push_back(pair);
587  }
588  else {
589  (*where).first++;
590  }
591  }
592  return(retval);
593 }
594 
595 // Get individuals migrating from here.
596 std::vector<population::individual_type> base_island::get_emigrants()
597 {
598  return m_s_policy->select(m_pop);
599 }
600 
602 
610 std::ostream &operator<<(std::ostream &s, const base_island &isl)
611 {
612  s << isl.human_readable();
613  return s;
614 }
615 
616 }
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
std::string human_readable_terse() const
Return terse human readable representation of the island.
Root PaGMO namespace.
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base problem.
Definition: problem/base.h:62
void evolve_t(int)
Evolve island for a specified minimum amount of time.
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
virtual std::string get_name() const
Return a string identifying the island's type.
Base class for migration replacement policies.
Definition: base_r_policy.h:57
void interrupt()
Interrupt evolution.
Base class for migration selection policies.
Definition: base_s_policy.h:54
boost::shared_ptr< base_r_policy > base_r_policy_ptr
Shared pointer to base replacement policy.
Definition: base_r_policy.h:40
std::ostream & operator<<(std::ostream &s, const archipelago &a)
Overload stream operator for pagmo::archipelago.
migration::base_s_policy_ptr m_s_policy
Migration selection policy.
Definition: base_island.h:167
Base algorithm class.
migration::base_r_policy_ptr get_r_policy() const
Get a copy of the replacement policy.
migration::base_r_policy_ptr m_r_policy
Migration replacement policy.
Definition: base_island.h:169
virtual ~base_island()
Destructor.
std::size_t get_evolution_time() const
Return the total evolution time in milliseconds.
Base problem class.
Definition: problem/base.h:148
void evolve(int=1)
Evolve island n times.
Population class.
Definition: population.h:70
std::size_t m_evo_time
Total time spent by the island on evolution (in milliseconds).
Definition: base_island.h:165
virtual base_ptr clone() const =0
Clone method.
std::string human_readable() const
Return human readable representation of the island.
virtual void join() const
Join island.
algorithm::base_ptr get_algorithm() const
Return copy of the internal algorithm.
virtual void thread_entry()
Thread entry hook.
virtual base_ptr clone() const =0
Clone method.
algorithm::base_ptr m_algo
Algorithm.
Definition: base_island.h:159
base_island & operator=(const base_island &)
Assignment operator.
Base island class.
Definition: base_island.h:81
migration::base_s_policy_ptr get_s_policy() const
Get a copy of the selection policy.
void set_algorithm(const algorithm::base &)
Algorithm setter.
archipelago * m_archi
Pointer that, if not null, points to the archipelago containing the island.
Definition: base_island.h:163
void set_v(population::size_type, const decision_vector &)
Sets a velocity vector.
virtual void thread_exit()
Thread exit hook.
problem::base_ptr get_problem() const
Return copy of the internal problem.
void set_population(const population &)
Set internal population.
boost::shared_ptr< base_s_policy > base_s_policy_ptr
Shared pointer to base selection policy.
Definition: base_s_policy.h:39
population m_pop
Population.
Definition: base_island.h:161
container_type::size_type size_type
Population size type.
Definition: population.h:192
void set_x(population::size_type, const decision_vector &)
Sets a decision vector.
base_island(const base_island &)
Copy constructor.
Definition: base_island.cpp:75
population::size_type get_size() const
Size of the internal population.
bool busy() const
Query the status of the island.
boost::scoped_ptr< boost::thread > m_evo_thread
Evolution thread.
Definition: base_island.h:171
population get_population() const
Get a copy of the internal population.