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