All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Pages
simulated_annealing.hpp
Go to the documentation of this file.
1 //=======================================================================
2 // Copyright (c) 2013 Piotr Wygocki
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See
5 // accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //=======================================================================
15 #ifndef PAAL_SIMULATED_ANNEALING_HPP
16 #define PAAL_SIMULATED_ANNEALING_HPP
17 
18 #include "paal/utils/fast_exp.hpp"
20 
21 #include <chrono>
22 #include <random>
23 
24 namespace paal {
25 namespace local_search {
26 
27 
40 template <typename Duration = std::chrono::seconds,
41  typename Clock = std::chrono::system_clock>
43 
52  double startTemperature,
53  double endTemperature)
54  : m_duration(duration), m_start(Clock::now()),
55  m_multiplier(startTemperature),
56  m_base(std::pow(endTemperature / startTemperature,
57  1. / duration.count())) {}
58 
62  void restart() { m_start = Clock::now(); }
63 
69  double operator()() const {
70  return m_multiplier *
71  std::pow(m_base, std::chrono::duration_cast<Duration>(
72  Clock::now() - m_start).count());
73  }
74 
75  private:
76  Duration m_duration;
77  typename Clock::time_point m_start;
78  double m_multiplier;
79  double m_base;
80 };
81 
93 template <typename Clock = std::chrono::system_clock, typename Duration>
95  double startTemperature,
96  double endTemperature) {
98  duration, startTemperature, endTemperature);
99 }
100 
108 
117  double startTemperature, double multiplier,
118  double numberOFRoundsWithSameTemperature = 1)
119  : m_temperature(startTemperature), m_multiplier(multiplier),
120  m_number_of_rounds_with_same_temperature(
121  numberOFRoundsWithSameTemperature) {}
122 
128  double operator()() {
129  if (++m_cnt_from_last_multiply ==
130  m_number_of_rounds_with_same_temperature) {
131  m_temperature *= m_multiplier;
132  m_cnt_from_last_multiply = 0;
133  }
134  return m_temperature;
135  }
136 
137  private:
138  double m_temperature;
139  double m_multiplier;
140  int m_number_of_rounds_with_same_temperature;
141  int m_cnt_from_last_multiply = 0;
142 };
143 
169 template <typename Solution, typename ProbabilisticGain, typename GetMoves,
170  typename SetTemperature>
171 double start_temperature(Solution &solution, ProbabilisticGain gain,
172  GetMoves get_moves, SetTemperature set_temperature,
173  double acceptance_rate = 0.4,
174  int repeats_number = 1e3, double epsilon = 1e-4) {
175  assert(acceptance_rate >= 0. && acceptance_rate <= 1.);
176 
177  auto get_success_rate = [&](int t) {
178  set_temperature(t);
179  int number_of_success = 0;
180  int total = 0;
181  for (int i = 0; i < repeats_number; ++i) {
182  for (auto && move : get_moves(solution)) {
183  ++total;
184  if (detail::positive_delta(gain(solution, move))) {
185  ++number_of_success;
186  }
187  }
188  }
189  return double(number_of_success) / total;
190  };
191 
192  // compute lower and upper bound
193  double t = 1.;
194  double success_rate = get_success_rate(t);
195  double t_lower_bound{ t }, t_upper_bound{ t };
196  if (success_rate > acceptance_rate) {
197  do {
198  t /= 2;
199  success_rate = get_success_rate(t);
200  if (t < epsilon) {
201  return 0.;
202  }
203  } while (success_rate > acceptance_rate);
204  t_lower_bound = t;
205  t_upper_bound = 2 * t;
206  } else if (success_rate < acceptance_rate) {
207  do {
208  t *= 2;
209  success_rate = get_success_rate(t);
210  } while (success_rate < acceptance_rate);
211  t_lower_bound = t / 2;
212  t_upper_bound = t;
213  } else {
214  return t;
215  }
216 
217  // binary search to find appropriate temperature
218  while (t_upper_bound - t_lower_bound > epsilon) {
219  t = (t_upper_bound + t_lower_bound) / 2;
220  success_rate = get_success_rate(t);
221  if (success_rate > acceptance_rate) {
222  t_upper_bound = t;
223  } else if (success_rate < acceptance_rate) {
224  t_lower_bound = t;
225  } else {
226  return t;
227  }
228  }
229 
230  return (t_upper_bound + t_lower_bound) / 2;
231 }
232 
244 template <typename Gain, typename GetTemperature,
245  typename random_generator = std::default_random_engine>
247 
248 
256  simulated_annealing_gain_adaptor(Gain gain, GetTemperature getTemperature,
257  random_generator rand = random_generator())
258  : m_gain(gain), m_get_temperature(getTemperature), m_rand(rand),
259  m_distribution(0.0, 1.0) {}
260 
267  template <typename Delta> struct is_chosen {
273  static is_chosen make_chosen(Delta d) { return is_chosen(true, d); }
274 
280  static is_chosen make_unchosen(Delta d) { return is_chosen(false, d); }
281 
290  : m_is_chosen(false), m_delta(std::numeric_limits<Delta>::max()) {}
291 
299  bool operator<(is_chosen other) const {
300  if (m_is_chosen != other.m_is_chosen) {
301  return m_is_chosen < other.m_is_chosen;
302  } else {
303  return m_delta < other.m_delta;
304  }
305  }
306 
314  bool operator>(is_chosen other) const {
315  if (m_is_chosen != other.m_is_chosen) {
316  return m_is_chosen > other.m_is_chosen;
317  } else {
318  return m_delta > other.m_delta;
319  }
320  }
322  bool chosen() const {
323  return m_is_chosen;
324  }
326  bool delta() const {
327  return m_delta;
328  }
329 
330 
331  private:
339  is_chosen(bool isChosen, Delta d) : m_is_chosen(isChosen), m_delta(d) {}
340  bool m_is_chosen;
341  Delta m_delta;
342  };
343 
353  template <typename... Args>
354  auto operator()(Args &&... args)
355  ->is_chosen<typename std::result_of<Gain(Args...)>::type> {
356  auto delta = m_gain(std::forward<Args>(args)...);
357  using Delta = decltype(delta);
358  if (detail::positive_delta(delta)) {
359  return is_chosen<Delta>::make_chosen(delta);
360  } else {
361  if (m_distribution(m_rand) <
362  fast_exp(double(delta) / m_get_temperature())) {
363  return is_chosen<Delta>::make_chosen(delta);
364  } else {
365  return is_chosen<Delta>::make_unchosen(delta);
366  }
367  }
368  }
369 
370  private:
371  Gain m_gain;
372  GetTemperature m_get_temperature;
373  random_generator m_rand;
374  std::uniform_real_distribution<double> m_distribution;
375 };
376 
389 template <typename Gain, typename GetTemperature,
390  typename random_generator = std::default_random_engine>
391 auto make_simulated_annealing_gain_adaptor(Gain gain, GetTemperature getTemperature,
392  random_generator rand =
393  random_generator()) {
394  return simulated_annealing_gain_adaptor<Gain, GetTemperature,
395  random_generator>(
396  std::move(gain), std::move(getTemperature), std::move(rand));
397 }
398 
412 template <typename Commit, typename Gain, typename GetTemperature,
413  typename random_generator = std::default_random_engine>
415 
418  GetTemperature get_temperature,
419  random_generator rand =
420  random_generator())
421  : m_commit(commit),
423  std::move(gain), std::move(get_temperature), std::move(rand))) {}
424 
433  template <typename... Args> bool operator()(Args &&... args) {
434  if (detail::positive_delta(m_gain(args...))) {
435  return m_commit(std::forward<Args>(args)...);
436  } else {
437  return false;
438  }
439  }
440 
441  private:
442  Commit m_commit;
444  m_gain;
445 };
446 
455 template <typename Commit, typename Gain, typename GetTemperature,
456  typename random_generator = std::default_random_engine>
458  GetTemperature getTemperature,
459  random_generator rand =
460  random_generator{}) {
461  return simulated_annealing_commit_adaptor<Commit, Gain, GetTemperature,
462  random_generator>(
463  std::move(commit), std::move(gain), std::move(getTemperature),
464  std::move(rand));
465 }
466 
467 }
468 }
469 
470 #endif // PAAL_SIMULATED_ANNEALING_HPP
auto operator()(Args &&...args) -> is_chosen< typename std::result_of< Gain(Args...)>::type >
operator(), returns original gain with information indicating if the move is chosen ...
This functors returns potential (temperature) using the following schema. The start potential equals ...
simulated_annealing_commit_adaptor(Commit commit, Gain gain, GetTemperature get_temperature, random_generator rand=random_generator())
contructor
simulated_annealing_gain_adaptor(Gain gain, GetTemperature getTemperature, random_generator rand=random_generator())
constructor
exponential_cooling_schema_dependant_on_iteration(double startTemperature, double multiplier, double numberOFRoundsWithSameTemperature=1)
Constructor.
This object represents Delta computed by gain and the decision if the object is taken or not...
exponential_cooling_schema_dependant_on_time(Duration duration, double startTemperature, double endTemperature)
Constructor.
This functors returns potential (temperature) using the following schema. The start potential equals ...
static is_chosen make_chosen(Delta d)
creates is_chosen for Move object
double operator()()
operator(), return Temperature at given time point point
idea is NOT mine, found in the web
This adaptor takes Gain functor and adopts it to simulated annealing. For each move, if it has positive gain it is chosen otherwise the move is chosen wit probability e^(movesGain/Temperature). Temperature is given by getTemperature functor.
auto make_simulated_annealing_gain_adaptor(Gain gain, GetTemperature getTemperature, random_generator rand=random_generator())
make function for simulated_annealing_gain_adaptor
double operator()() const
operator(), return Temperature at given time point point
auto make_simulated_annealing_commit_adaptor(Commit commit, Gain gain, GetTemperature getTemperature, random_generator rand=random_generator{})
make for simulated_annealing_commit_adaptor
float fast_exp(float p)
fast power of e.
Definition: fast_exp.hpp:47
bool local_search(Solution &solution, SearchStrategy searchStrategy, ContinueOnSuccess succ, ContinueOnFail fail, components...comps)
detail
bool operator<(is_chosen other) const
operator&lt;. Chosen move is always bigger then unchosen
This adaptor takes Cammit functor and adopts it to simulated annealing. If the input move has positiv...
static is_chosen make_unchosen(Delta d)
creates is_chosen for Move which is not chosen
double start_temperature(Solution &solution, ProbabilisticGain gain, GetMoves get_moves, SetTemperature set_temperature, double acceptance_rate=0.4, int repeats_number=1e3, double epsilon=1e-4)
This function takes gain functor which is assumed to be probabilistic and dependent on one nonnegativ...
is_chosen()
constructor (from 0) represents the largest possible value of unchosen element If some element is big...
auto make_exponential_cooling_schema_dependant_on_time(Duration duration, double startTemperature, double endTemperature)
make function for exponential_cooling_schema_dependant_on_time