COIN-OR::LEMON - Graph Library

source: lemon-0.x/src/work/akos/simann.h @ 1142:450f794dca81

Last change on this file since 1142:450f794dca81 was 1142:450f794dca81, checked in by Akos Ladanyi, 17 years ago

more docs

File size: 9.2 KB
Line 
1#ifndef LEMON_SIMANN_H
2#define LEMON_SIMANN_H
3
4/// \ingroup experimental
5/// \file
6/// \brief Simulated annealing framework.
7/// \author Akos Ladanyi
8
9#include <cstdlib>
10#include <cmath>
11#include <lemon/time_measure.h>
12
13namespace lemon {
14
15/// \addtogroup experimental
16/// @{
17
18  const double INFTY = 1e24;
19
20  /*! \brief Simulated annealing base class. */
21  class SimAnnBase {
22  public:
23    class Controller;
24  private:
25    /*! Pointer to the controller. */
26    Controller *controller;
27  protected:
28    /*! \brief Cost of the current solution. */
29    double curr_cost;
30    /*! \brief Cost of the best solution. */
31    double best_cost;
32    /*! \brief Cost of the previous solution. */
33    double prev_cost;
34    /*! \brief Cost of the solution preceding the previous one. */
35    double prev_prev_cost;
36
37    /*! \brief Step to a neighbouring state. */
38    virtual void mutate() {}
39    /*! \brief Reverts the last mutate(). */
40    virtual void revert() {}
41    /*! \brief Saves the current solution as the best one. */
42    virtual void saveAsBest() {}
43  public:
44    /*! \brief Constructor. */
45    SimAnnBase() {
46      best_cost = prev_cost = prev_prev_cost = INFTY;
47    }
48    /*! \brief Sets the controller class to use. */
49    void setController(Controller &_controller) {
50      controller = &_controller;
51      controller->setBase(this);
52    }
53    /*! \brief Returns the cost of the current solution. */
54    double getCurrCost() const { return curr_cost; }
55    /*! \brief Returns the cost of the previous solution. */
56    double getPrevCost() const { return prev_cost; }
57    /*! \brief Returns the cost of the best solution. */
58    double getBestCost() const { return best_cost; }
59    /*! \brief Starts the annealing process. */
60    void run() {
61      controller->init();
62      do {
63        mutate();
64        if (controller->accept()) {
65          controller->acceptEvent();
66          if (curr_cost < best_cost) {
67            saveAsBest();
68            controller->improveEvent();
69          }
70        }
71        else {
72          revert();
73          controller->rejectEvent();
74        }
75      } while (controller->next());
76    }
77
78    /*! \brief A base class for controllers. */
79    class Controller {
80    public:
81      /*! \brief Pointer to the simulated annealing base class. */
82      SimAnnBase *base;
83      /*! \brief Initializes the controller. */
84      virtual void init() {}
85      /*! \brief This is called when a neighbouring state gets accepted. */
86      virtual void acceptEvent() {}
87      /*! \brief This is called when the accepted neighbouring state's cost is
88       *  less than the best found one's.
89       */
90      virtual void improveEvent() {}
91      /*! \brief This is called when a neighbouring state gets rejected. */
92      virtual void rejectEvent() {}
93      /*! \brief Sets the simulated annealing base class to use. */
94      virtual void setBase(SimAnnBase *_base) { base = _base; }
95      /*! \brief Decides whether to continue the annealing process or not. */
96      virtual bool next() = 0;
97      /*! \brief Decides whether to accept the current solution or not. */
98      virtual bool accept() = 0;
99    };
100  };
101
102  /*! \brief Simulated annealing class. */
103  template <typename E>
104  class SimAnn : public SimAnnBase {
105  private:
106    /*! \brief Pointer to the current entity. */
107    E *curr_ent;
108    /*! \brief Pointer to the best entity. */
109    E *best_ent;
110  public:
111    /*! \brief Constructor. */
112    SimAnn() : SimAnnBase() {}
113    /*! \brief Sets the initial entity. */
114    void setEntity(E &ent) {
115      curr_ent = new E(ent);
116      best_ent = new E(ent);
117      curr_cost = curr_ent->getCost();
118    }
119    /*! \brief Returns the best found entity. */
120    E getBestEntity() { return *best_ent; }
121    /*! \brief Step to a neighbouring state. */
122    void mutate() {
123      prev_prev_cost = prev_cost;
124      prev_cost = curr_cost;
125      curr_ent->mutate();
126      curr_cost = curr_ent->getCost();
127    }
128    /*! \brief Reverts the last mutate(). */
129    void revert() {
130      curr_ent->revert();
131      curr_cost = prev_cost;
132      prev_cost = prev_prev_cost;
133    }
134    /*! \brief Saves the current solution as the best one. */
135    void saveAsBest() {
136      delete(best_ent);
137      best_ent = new E(*curr_ent);
138      best_cost = curr_cost;
139    }
140  };
141
142  /*! \brief Skeleton of an entity class. */
143  class EntitySkeleton {
144  public:
145    /*! \brief Returns the cost of the entity. */
146    double getCost() { return 0.0; }
147    /*! \brief Makes a minor change to the entity. */
148    void mutate() {}
149    /*! \brief Restores the entity to its previous state i.e. reverts the
150     *  effects of the last mutate().
151     */
152    void revert() {}
153  };
154
155  /*! \brief A simple controller for the simulated annealing class. */
156  class SimpleController : public SimAnnBase::Controller {
157  public:
158    /*! \brief Number of iterations. */
159    long iter;
160    /*! \brief Number of iterations which did not improve the solution since
161     *  the last improvement. */
162    long last_impr;
163    /*! \brief Maximum number of iterations. */
164    long max_iter;
165    /*! \brief Maximum number of iterations which do not improve the
166     *  solution. */
167    long max_no_impr;
168    /*! \brief Temperature. */
169    double temp;
170    /*! \brief Annealing factor. */
171    double ann_fact;
172    /*! \brief Constructor.
173     *  \param _max_iter maximum number of iterations
174     *  \param _max_no_impr maximum number of consecutive iterations which do
175     *         not yield a better solution
176     *  \param _temp initial temperature
177     *  \param _ann_fact annealing factor
178     */
179    SimpleController(long _max_iter = 500000, long _max_no_impr = 20000,
180    double _temp = 1000.0, double _ann_fact = 0.9999) : iter(0), last_impr(0),
181    max_iter(_max_iter), max_no_impr(_max_no_impr), temp(_temp),
182    ann_fact(_ann_fact) {}
183    void acceptEvent() {
184      iter++;
185    }
186    /*! \brief This is called when the accepted neighbouring state's cost is
187     *  less than the best found one's.
188     */
189    void improveEvent() {
190      last_impr = iter;
191    }
192    /*! \brief This is called when a neighbouring state gets rejected. */
193    void rejectEvent() {
194      iter++;
195    }
196    /*! \brief Decides whether to continue the annealing process or not. Also
197     *  decreases the temperature. */
198    bool next() {
199      temp *= ann_fact;
200      bool quit = (iter > max_iter) || (iter - last_impr > max_no_impr);
201      return !quit;
202    }
203    /*! \brief Decides whether to accept the current solution or not. */
204    bool accept() {
205      double cost_diff = base->getPrevCost() - base->getCurrCost();
206      if (cost_diff < 0.0) {
207        bool ret = drand48() <= exp(cost_diff / temp);
208        return ret;
209      }
210      else {
211        return true;
212      }
213    }
214  };
215
216  /*! \brief A controller with preset running time for the simulated annealing
217   *  class.
218   */
219  class AdvancedController : public SimAnnBase::Controller {
220  private:
221    Timer timer;
222    /*! \param time the elapsed time in seconds */
223    virtual double threshold(double time) {
224      return (-1.0) * start_threshold / end_time * time + start_threshold;
225    }
226  public:
227    double alpha;
228    double beta;
229    double gamma;
230    double end_time;
231    double start_time;
232    double start_threshold;
233    double avg_cost;
234    double temp;
235    double ann_fact;
236    bool warmup;
237    /*! \brief Constructor.
238     *  \param _end_time running time in seconds
239     *  \param _alpha parameter used to calculate the running average
240     *  \param _beta parameter used to decrease the annealing factor
241     *  \param _gamma parameter used to increase the temperature
242     */
243    AdvancedController(double _end_time, double _alpha = 0.2,
244    double _beta = 0.9, double _gamma = 1.6) : alpha(_alpha), beta(_beta),
245    gamma(_gamma), end_time(_end_time), ann_fact(0.99999999), warmup(true) {}
246    void init() {
247      avg_cost = base->getCurrCost();
248    }
249    /*! \brief This is called when a neighbouring state gets accepted. */
250    void acceptEvent() {
251      avg_cost = alpha * base->getCurrCost() + (1.0 - alpha) * avg_cost;
252      if (warmup) {
253        static int cnt = 0;
254        cnt++;
255        if (cnt >= 100) {
256          // calculate starting threshold and starting temperature
257          start_threshold = 5.0 * fabs(base->getBestCost() - avg_cost);
258          temp = 10000.0;
259          warmup = false;
260          timer.reset();
261        }
262      }
263    }
264    /*! \brief Decides whether to continue the annealing process or not. */
265    bool next() {
266      if (warmup) {
267        return true;
268      }
269      else {
270        double elapsed_time = timer.getRealTime();
271        if (fabs(avg_cost - base->getBestCost()) > threshold(elapsed_time)) {
272          // decrease the annealing factor
273          ann_fact *= beta;
274        }
275        else {
276          // increase the temperature
277          temp *= gamma;
278          ann_fact = 0.99999999; // !!!!!!!!!!!
279        }
280        temp *= ann_fact;
281        return elapsed_time < end_time;
282      }
283    }
284    /*! \brief Decides whether to accept the current solution or not. */
285    bool accept() {
286      if (warmup) {
287        // we accept eveything during the "warm up" phase
288        return true;
289      }
290      else {
291        double cost_diff = base->getPrevCost() - base->getCurrCost();
292        if (cost_diff < 0.0) {
293          return (drand48() <= exp(cost_diff / temp));
294        }
295        else {
296          return true;
297        }
298      }
299    }
300  };
301
302/// @}
303
304}
305
306#endif
Note: See TracBrowser for help on using the repository browser.