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