COIN-OR::LEMON - Graph Library

source: lemon-0.x/src/work/akos/simann.h @ 1018:68beae6758a7

Last change on this file since 1018:68beae6758a7 was 1018:68beae6758a7, checked in by Akos Ladanyi, 19 years ago

Use lemon::Timer for time measuring. Added the threshold() function and initial threshold and temperature calculation.

File size: 6.9 KB
RevLine 
[942]1#ifndef LEMON_SIMANN_H
2#define LEMON_SIMANN_H
[918]3
[966]4#include <cstdlib>
5#include <cmath>
[1018]6#include <lemon/time_measure.h>
[966]7
[942]8namespace lemon {
[918]9
[942]10  const double INFTY = 1e24;
[918]11
[942]12  class SimAnnBase {
[918]13  public:
[942]14    class Controller;
15  private:
16    Controller *controller;
17  protected:
18    double curr_cost;
19    double prev_cost;
20    double best_cost;
[918]21
[942]22    virtual void mutate() = 0;
23    virtual void revert() = 0;
24    virtual void saveAsBest() = 0;
25  public:
26    SimAnnBase() {
27      curr_cost = prev_cost = best_cost = INFTY;
28    }
[957]29    void setController(Controller &_controller) {
30      controller = &_controller;
31      controller->setBase(this);
32    }
[1018]33    double getCurrCost() const { return curr_cost; }
34    double getPrevCost() const { return prev_cost; }
35    double getBestCost() const { return best_cost; }
[942]36    void run() {
[966]37      controller->init();
[1018]38      do {
[942]39        mutate();
[957]40        if (controller->accept()) {
[942]41          controller->acceptEvent();
42          if (curr_cost < best_cost) {
43            saveAsBest();
44            controller->improveEvent();
45          }
46        }
47        else {
48          revert();
49          controller->rejectEvent();
50        }
[1018]51      } while (controller->next());
[918]52    }
53
[1000]54    /*! \brief A base class for controllers. */
[942]55    class Controller {
56    public:
[957]57      SimAnnBase *base;
[966]58      virtual void init() {}
[1000]59      /*! \brief This is called when a neighbouring state gets accepted. */
[942]60      virtual void acceptEvent() {}
[1000]61      /*! \brief This is called when the accepted neighbouring state's cost is
62       *  less than the best found one's.
63       */
[942]64      virtual void improveEvent() {}
[1000]65      /*! \brief This is called when a neighbouring state gets rejected. */
[942]66      virtual void rejectEvent() {}
[957]67      virtual void setBase(SimAnnBase *_base) { base = _base; }
[1000]68      /*! */
[942]69      virtual bool next() = 0;
[1000]70      /*! */
[957]71      virtual bool accept() = 0;
[942]72    };
73  };
[918]74
[1018]75  /*! \todo atgondolni mi is ez a prev_cost */
[942]76  template <typename E>
77  class SimAnn : public SimAnnBase {
78  private:
79    E *curr_ent;
80    E *best_ent;
81  public:
[957]82    SimAnn() : SimAnnBase() {}
83    void setEntity(E &ent) {
84      curr_ent = new E(ent);
85      best_ent = new E(ent);
[942]86    }
87    E getBestEntity() { return *best_ent; }
88    void mutate() {
[1018]89      prev_cost = curr_cost;
90      curr_cost = curr_ent->mutate();
[942]91    }
92    void revert() {
93      curr_ent->revert();
[1018]94      curr_cost = prev_cost;
[942]95    }
96    void saveAsBest() {
97      *best_ent = *curr_ent;
98      best_cost = curr_cost;
99    }
100  };
101
[956]102  class EntitySkeleton {
[942]103  public:
[966]104    /*! \brief Makes a minor change to the entity.
105     *  \return the new cost
106     */
[942]107    double mutate() { return 0.0; }
[966]108    /*! \brief Restores the entity to its previous state i.e. reverts the
109     *  effects of the last mutate.
110     */
[942]111    void revert() {}
112  };
113
[966]114  /*! \brief A simple controller for the simulated annealing class.
115   *  \todo Find a way to set the various parameters.
116   */
[956]117  class SimpleController : public SimAnnBase::Controller {
118  public:
119    long iter, last_impr, max_iter, max_no_impr;
[1000]120    double temp, ann_fact;
121    /*! \param _max_iter maximum number of iterations
122     *  \param _max_no_impr maximum number of consecutive iterations which do
123     *         not yield a better solution
124     *  \param _temp initial temperature
125     *  \param _ann_fact annealing factor
126     */
127    SimpleController(long _max_iter = 500000, long _max_no_impr = 20000,
128    double _temp = 1000, double _ann_fact = 0.9999) : iter(0), last_impr(0),
129    max_iter(_max_iter), max_no_impr(_max_no_impr), temp(_temp),
130    ann_fact(_ann_fact) {}
[956]131    void acceptEvent() {
132      iter++;
133    }
134    void improveEvent() {
135      last_impr = iter;
136    }
137    void rejectEvent() {
138      iter++;
139    }
140    bool next() {
[1000]141      temp *= ann_fact;
[956]142      bool quit = (iter > max_iter) || (iter - last_impr > max_no_impr);
143      return !quit;
144    }
[957]145    bool accept() {
[1018]146      double cost_diff = base->getPrevCost() - base->getCurrCost();
147      if (cost_diff < 0.0) {
148        return (drand48() <= exp(cost_diff / temp));
149      }
150      else {
151        return true;
152      }
[966]153    }
154  };
155
156  /*! \brief A controller with preset running time for the simulated annealing
157   *  class.
158   *  \todo Find a better name.
159   */
160  class AdvancedController : public SimAnnBase::Controller {
161  private:
[1018]162    Timer timer;
[1000]163    /*! \param time the elapsed time in seconds */
[1018]164    virtual double threshold(double time) {
165      // this is the function 1 / log(x) scaled and offset
166      static double xm = 5.0 / end_time;
167      static double ym = start_threshold / (1 / log(1.2) - 1 / log(5.0 + 1.2));
168      return ym * (1 / log(xm * time + 1.2) - 1 / log(5.0 + 1.2));
169    }
[966]170  public:
[1000]171    double alpha, beta, gamma;
172    double end_time, start_time;
[1018]173    double start_threshold;
[966]174    double avg_cost;
[1000]175    double temp, ann_fact;
[1018]176    bool warmup;
177    long iter;
178    /*! \param _end_time running time in seconds
[1000]179     *  \param _alpha parameter used to calculate the running average
180     *  \param _beta parameter used to decrease the annealing factor
181     *  \param _gamma parameter used to increase the temperature
182     */
183    AdvancedController(double _end_time, double _alpha = 0.2,
184    double _beta = 0.9, double _gamma = 1.2) : alpha(_alpha), beta(_beta),
[1018]185    gamma(_gamma), end_time(_end_time), ann_fact(0.9999), warmup(true),
186    iter(0) {}
[966]187    void init() {
[1018]188      avg_cost = base->getCurrCost();
[966]189    }
190    void acceptEvent() {
191      avg_cost = alpha * base->getCurrCost() + (1.0 - alpha) * avg_cost;
[1018]192      iter++;
[966]193    }
194    void improveEvent() {
195    }
196    void rejectEvent() {
[1018]197      iter++;
[966]198    }
199    bool next() {
[1018]200      if (warmup) {
201        static double max_cost_diff = 0.0;
202        double cost_diff = base->getCurrCost() - base->getPrevCost();
203        // jo ez igy egyaltalan? -> prev_cost
204        if ((cost_diff > 0.0) && (cost_diff > max_cost_diff)) {
205          max_cost_diff = cost_diff;
206        }
207        // How to set the starting temperature when all the 100 first
208        // iterations improve the solution?
209        if (iter > 100) {
210          // calculate starting threshold and starting temperature
211          start_threshold = fabs(base->getBestCost() - avg_cost);
212          temp = exp(max_cost_diff) / 0.5;
213          warmup = false;
214          timer.reset();
215        }
216        return true;
[1000]217      }
218      else {
[1018]219        double elapsed_time = timer.getRealTime();
220        if (fabs(avg_cost - base->getBestCost()) > threshold(elapsed_time)) {
221          // decrease the annealing factor
222          ann_fact *= beta;
223        }
224        else {
225          // increase the temperature
226          temp *= gamma;
227        }
228        temp *= ann_fact;
229        return elapsed_time < end_time;
[1000]230      }
[966]231    }
232    bool accept() {
[1018]233      if (warmup) {
234        // we accept eveything during the "warm up" phase
235        return true;
236      }
237      else {
238        double cost_diff = base->getPrevCost() - base->getCurrCost();
239        if (cost_diff < 0.0) {
240          return (drand48() <= exp(cost_diff / temp));
241        }
242        else {
243          return true;
244        }
245      }
[956]246    }
247  };
248
[942]249}
[918]250
251#endif
Note: See TracBrowser for help on using the repository browser.