src/work/akos/simann.h
author alpar
Sat, 20 Nov 2004 10:19:06 +0000
changeset 1011 5bd6c7671c9e
parent 966 5e865c5c8a87
child 1018 68beae6758a7
permissions -rw-r--r--
- snapshot-rollback functionarity added to ListGraph
- The iterface of the snapshot-rollback functionarity in SmartGraph has
changed to be compatible with ListGraph::SnapShot.
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@966
     6
#include <sys/time.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@957
    33
    double getCurrCost() { return curr_cost; }
ladanyi@957
    34
    double getPrevCost() { return prev_cost; }
ladanyi@942
    35
    double getBestCost() { return best_cost; }
ladanyi@942
    36
    void run() {
ladanyi@966
    37
      controller->init();
ladanyi@942
    38
      while (controller->next()) {
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@918
    51
      }
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@942
    75
  template <typename E>
ladanyi@942
    76
  class SimAnn : public SimAnnBase {
ladanyi@942
    77
  private:
ladanyi@942
    78
    E *curr_ent;
ladanyi@942
    79
    E *best_ent;
ladanyi@942
    80
  public:
ladanyi@957
    81
    SimAnn() : SimAnnBase() {}
ladanyi@957
    82
    void setEntity(E &ent) {
ladanyi@957
    83
      curr_ent = new E(ent);
ladanyi@957
    84
      best_ent = new E(ent);
ladanyi@942
    85
    }
ladanyi@942
    86
    E getBestEntity() { return *best_ent; }
ladanyi@942
    87
    void mutate() {
ladanyi@942
    88
      curr_ent->mutate();
ladanyi@942
    89
    }
ladanyi@942
    90
    void revert() {
ladanyi@942
    91
      curr_ent->revert();
ladanyi@942
    92
    }
ladanyi@942
    93
    void saveAsBest() {
ladanyi@942
    94
      *best_ent = *curr_ent;
ladanyi@942
    95
      best_cost = curr_cost;
ladanyi@942
    96
    }
ladanyi@942
    97
  };
ladanyi@942
    98
ladanyi@956
    99
  class EntitySkeleton {
ladanyi@942
   100
  public:
ladanyi@966
   101
    /*! \brief Makes a minor change to the entity.
ladanyi@966
   102
     *  \return the new cost
ladanyi@966
   103
     */
ladanyi@942
   104
    double mutate() { return 0.0; }
ladanyi@966
   105
    /*! \brief Restores the entity to its previous state i.e. reverts the
ladanyi@966
   106
     *  effects of the last mutate.
ladanyi@966
   107
     */
ladanyi@942
   108
    void revert() {}
ladanyi@942
   109
  };
ladanyi@942
   110
ladanyi@966
   111
  /*! \brief A simple controller for the simulated annealing class.
ladanyi@966
   112
   *  \todo Find a way to set the various parameters.
ladanyi@966
   113
   */
ladanyi@956
   114
  class SimpleController : public SimAnnBase::Controller {
ladanyi@956
   115
  public:
ladanyi@956
   116
    long iter, last_impr, max_iter, max_no_impr;
ladanyi@1000
   117
    double temp, ann_fact;
ladanyi@1000
   118
    /*! \param _max_iter maximum number of iterations
ladanyi@1000
   119
     *  \param _max_no_impr maximum number of consecutive iterations which do
ladanyi@1000
   120
     *         not yield a better solution
ladanyi@1000
   121
     *  \param _temp initial temperature
ladanyi@1000
   122
     *  \param _ann_fact annealing factor
ladanyi@1000
   123
     */
ladanyi@1000
   124
    SimpleController(long _max_iter = 500000, long _max_no_impr = 20000,
ladanyi@1000
   125
    double _temp = 1000, double _ann_fact = 0.9999) : iter(0), last_impr(0),
ladanyi@1000
   126
    max_iter(_max_iter), max_no_impr(_max_no_impr), temp(_temp),
ladanyi@1000
   127
    ann_fact(_ann_fact) {}
ladanyi@956
   128
    void acceptEvent() {
ladanyi@956
   129
      iter++;
ladanyi@956
   130
    }
ladanyi@956
   131
    void improveEvent() {
ladanyi@956
   132
      last_impr = iter;
ladanyi@956
   133
    }
ladanyi@956
   134
    void rejectEvent() {
ladanyi@956
   135
      iter++;
ladanyi@956
   136
    }
ladanyi@956
   137
    bool next() {
ladanyi@1000
   138
      temp *= ann_fact;
ladanyi@956
   139
      bool quit = (iter > max_iter) || (iter - last_impr > max_no_impr);
ladanyi@956
   140
      return !quit;
ladanyi@956
   141
    }
ladanyi@957
   142
    bool accept() {
ladanyi@966
   143
      return (drand48() <= exp(base->getPrevCost() - base->getCurrCost() /
ladanyi@966
   144
        temp));
ladanyi@966
   145
    }
ladanyi@966
   146
  };
ladanyi@966
   147
ladanyi@966
   148
  /*! \brief A controller with preset running time for the simulated annealing
ladanyi@966
   149
   *  class.
ladanyi@966
   150
   *  \todo Find a better name.
ladanyi@966
   151
   */
ladanyi@966
   152
  class AdvancedController : public SimAnnBase::Controller {
ladanyi@966
   153
  private:
ladanyi@1000
   154
    /*! \param time the elapsed time in seconds */
ladanyi@1000
   155
    double threshold(double time) { return 0.0; }
ladanyi@966
   156
  public:
ladanyi@1000
   157
    double alpha, beta, gamma;
ladanyi@1000
   158
    double end_time, start_time;
ladanyi@966
   159
    double avg_cost;
ladanyi@1000
   160
    double temp, ann_fact;
ladanyi@1000
   161
    /*! \param _end_time running_time
ladanyi@1000
   162
     *  \param _alpha parameter used to calculate the running average
ladanyi@1000
   163
     *  \param _beta parameter used to decrease the annealing factor
ladanyi@1000
   164
     *  \param _gamma parameter used to increase the temperature
ladanyi@1000
   165
     */
ladanyi@1000
   166
    AdvancedController(double _end_time, double _alpha = 0.2,
ladanyi@1000
   167
    double _beta = 0.9, double _gamma = 1.2) : alpha(_alpha), beta(_beta),
ladanyi@1000
   168
    gamma(_gamma), end_time(_end_time) {}
ladanyi@966
   169
    void init() {
ladanyi@966
   170
      timeval tv;
ladanyi@966
   171
      gettimeofday(&tv, 0);
ladanyi@966
   172
      start_time = tv.tv_sec + double(tv.tv_usec) / 1e6;
ladanyi@966
   173
    }
ladanyi@966
   174
    void acceptEvent() {
ladanyi@966
   175
      avg_cost = alpha * base->getCurrCost() + (1.0 - alpha) * avg_cost;
ladanyi@966
   176
    }
ladanyi@966
   177
    void improveEvent() {
ladanyi@966
   178
    }
ladanyi@966
   179
    void rejectEvent() {
ladanyi@966
   180
    }
ladanyi@966
   181
    bool next() {
ladanyi@966
   182
      timeval tv;
ladanyi@966
   183
      gettimeofday(&tv, 0);
ladanyi@966
   184
      double elapsed_time = tv.tv_sec + double(tv.tv_usec) / 1e6 - start_time;
ladanyi@1000
   185
      if (fabs(avg_cost - base->getBestCost()) > threshold(elapsed_time)) {
ladanyi@1000
   186
	// decrease the annealing factor
ladanyi@1000
   187
        ann_fact *= beta;
ladanyi@1000
   188
      }
ladanyi@1000
   189
      else {
ladanyi@1000
   190
        // increase the temperature
ladanyi@1000
   191
        temp *= gamma;
ladanyi@1000
   192
      }
ladanyi@1000
   193
      temp *= ann_fact;
ladanyi@966
   194
      return elapsed_time < end_time;
ladanyi@966
   195
    }
ladanyi@966
   196
    bool accept() {
ladanyi@966
   197
      return (drand48() <= exp(base->getPrevCost() - base->getCurrCost() /
ladanyi@966
   198
        temp));
ladanyi@956
   199
    }
ladanyi@956
   200
  };
ladanyi@956
   201
ladanyi@942
   202
}
ladanyi@918
   203
ladanyi@918
   204
#endif