lemon/simann.h
author hegyi
Mon, 21 Nov 2005 18:03:20 +0000
changeset 1823 cb082cdf3667
child 1847 7cbc12e42482
permissions -rw-r--r--
NewMapWin has become Dialog instead of Window. Therefore it is created dynamically, when there is need for it, instead of keeping one instance in memory. This solution is slower, but more correct than before.
alpar@1633
     1
#ifndef LEMON_SIMANN_H
alpar@1633
     2
#define LEMON_SIMANN_H
alpar@1633
     3
alpar@1633
     4
/// \ingroup experimental
alpar@1633
     5
/// \file
alpar@1633
     6
/// \brief Simulated annealing framework.
alpar@1633
     7
/// \author Akos Ladanyi
alpar@1633
     8
alpar@1633
     9
#include <cstdlib>
alpar@1633
    10
#include <cmath>
alpar@1633
    11
#include <lemon/time_measure.h>
alpar@1633
    12
alpar@1633
    13
namespace lemon {
alpar@1633
    14
alpar@1633
    15
/// \addtogroup experimental
alpar@1633
    16
/// @{
alpar@1633
    17
alpar@1633
    18
  /*! \brief A base class for controllers. */
alpar@1633
    19
  class ControllerBase {
alpar@1633
    20
    friend class SimAnnBase;
alpar@1633
    21
  public:
alpar@1633
    22
    /*! \brief Pointer to the simulated annealing base class. */
alpar@1633
    23
    SimAnnBase *simann;
alpar@1633
    24
    /*! \brief Initializes the controller. */
alpar@1633
    25
    virtual void init() {}
alpar@1633
    26
    /*! \brief This is called when a neighbouring state gets accepted. */
alpar@1633
    27
    virtual void acceptEvent() {}
alpar@1633
    28
    /*! \brief This is called when the accepted neighbouring state's cost is
alpar@1633
    29
     *  less than the best found one's.
alpar@1633
    30
     */
alpar@1633
    31
    virtual void improveEvent() {}
alpar@1633
    32
    /*! \brief This is called when a neighbouring state gets rejected. */
alpar@1633
    33
    virtual void rejectEvent() {}
alpar@1633
    34
    /*! \brief Decides whether to continue the annealing process or not. */
alpar@1633
    35
    virtual bool next() = 0;
alpar@1633
    36
    /*! \brief Decides whether to accept the current solution or not. */
alpar@1633
    37
    virtual bool accept() = 0;
alpar@1633
    38
  };
alpar@1633
    39
alpar@1633
    40
  /*! \brief Skeleton of an entity class. */
alpar@1633
    41
  class EntityBase {
alpar@1633
    42
  public:
alpar@1633
    43
    /*! \brief Makes a minor change to the entity.
alpar@1633
    44
     *  \return the new cost
alpar@1633
    45
     */
alpar@1633
    46
    virtual double mutate() = 0;
alpar@1633
    47
    /*! \brief Restores the entity to its previous state i.e. reverts the
alpar@1633
    48
     *  effects of the last mutate().
alpar@1633
    49
     */
alpar@1633
    50
    virtual void revert() = 0;
alpar@1633
    51
    /*! \brief Makes a copy of the entity. */
alpar@1633
    52
    virtual EntityBase* clone() = 0;
alpar@1633
    53
    /*! \brief Makes a major change to the entity. */
alpar@1633
    54
    virtual void randomize() = 0;
alpar@1633
    55
  };
alpar@1633
    56
alpar@1633
    57
  /*! \brief Simulated annealing base class. */
alpar@1633
    58
  class SimAnnBase {
alpar@1633
    59
  private:
alpar@1633
    60
    /*! Pointer to the controller. */
alpar@1633
    61
    ControllerBase *controller;
alpar@1633
    62
    /*! \brief Cost of the current solution. */
alpar@1633
    63
    double curr_cost;
alpar@1633
    64
    /*! \brief Cost of the best solution. */
alpar@1633
    65
    double best_cost;
alpar@1633
    66
    /*! \brief Cost of the previous solution. */
alpar@1633
    67
    double prev_cost;
alpar@1633
    68
    /*! \brief Cost of the solution preceding the previous one. */
alpar@1633
    69
    double prev_prev_cost;
alpar@1633
    70
    /*! \brief Number of iterations. */
alpar@1633
    71
    long iter;
alpar@1633
    72
    /*! \brief Number of iterations which did not improve the solution since
alpar@1633
    73
     *  the last improvement. */
alpar@1633
    74
    long last_impr;
alpar@1633
    75
  protected:
alpar@1633
    76
    /*! \brief Step to a neighbouring state. */
alpar@1633
    77
    virtual double mutate() = 0;
alpar@1633
    78
    /*! \brief Reverts the last mutate(). */
alpar@1633
    79
    virtual void revert() = 0;
alpar@1633
    80
    /*! \brief Saves the current solution as the best one. */
alpar@1633
    81
    virtual void saveAsBest() = 0;
alpar@1633
    82
    /*! \brief Does initializations before each run. */
alpar@1633
    83
    virtual void init() {
alpar@1633
    84
      controller->init();
alpar@1633
    85
      curr_cost = prev_cost = prev_prev_cost = best_cost =
alpar@1633
    86
        std::numeric_limits<double>::infinity();
alpar@1633
    87
      iter = last_impr = 0;
alpar@1633
    88
    }
alpar@1633
    89
  public:
alpar@1633
    90
    /*! \brief Sets the controller class to use. */
alpar@1633
    91
    void setController(ControllerBase &_controller) {
alpar@1633
    92
      controller = &_controller;
alpar@1633
    93
      controller->simann = this;
alpar@1633
    94
    }
alpar@1633
    95
    /*! \brief Returns the cost of the current solution. */
alpar@1633
    96
    double getCurrCost() const { return curr_cost; }
alpar@1633
    97
    /*! \brief Returns the cost of the previous solution. */
alpar@1633
    98
    double getPrevCost() const { return prev_cost; }
alpar@1633
    99
    /*! \brief Returns the cost of the best solution. */
alpar@1633
   100
    double getBestCost() const { return best_cost; }
alpar@1633
   101
    /*! \brief Returns the number of iterations. */
alpar@1633
   102
    long getIter() const { return iter; }
alpar@1633
   103
    /*! \brief Returns the number of the last iteration when the solution was
alpar@1633
   104
     *  improved.
alpar@1633
   105
     */
alpar@1633
   106
    long getLastImpr() const { return last_impr; }
alpar@1633
   107
    /*! \brief Performs one iteration. */
alpar@1633
   108
    bool step() {
alpar@1633
   109
      iter++;
alpar@1633
   110
      prev_prev_cost = prev_cost;
alpar@1633
   111
      prev_cost = curr_cost;
alpar@1633
   112
      curr_cost = mutate();
alpar@1633
   113
      if (controller->accept()) {
alpar@1633
   114
        controller->acceptEvent();
alpar@1633
   115
        last_impr = iter;
alpar@1633
   116
        if (curr_cost < best_cost) {
alpar@1633
   117
          best_cost = curr_cost;
alpar@1633
   118
          saveAsBest();
alpar@1633
   119
          controller->improveEvent();
alpar@1633
   120
        }
alpar@1633
   121
      }
alpar@1633
   122
      else {
alpar@1633
   123
        revert();
alpar@1633
   124
        curr_cost = prev_cost;
alpar@1633
   125
        prev_cost = prev_prev_cost;
alpar@1633
   126
        controller->rejectEvent();
alpar@1633
   127
      }
alpar@1633
   128
      return controller->next();
alpar@1633
   129
    }
alpar@1633
   130
    /*! \brief Performs a given number of iterations.
alpar@1633
   131
     *  \param n the number of iterations
alpar@1633
   132
     */
alpar@1633
   133
    bool step(int n) {
alpar@1633
   134
      for(; n > 0 && step(); --n) ;
alpar@1633
   135
      return !n;
alpar@1633
   136
    }
alpar@1633
   137
    /*! \brief Starts the annealing process. */
alpar@1633
   138
    void run() {
alpar@1633
   139
      init();
alpar@1633
   140
      do { } while (step());
alpar@1633
   141
    }
alpar@1633
   142
  };
alpar@1633
   143
alpar@1633
   144
  /*! \brief Simulated annealing class. */
alpar@1633
   145
  class SimAnn : public SimAnnBase {
alpar@1633
   146
  private:
alpar@1633
   147
    /*! \brief Pointer to the current entity. */
alpar@1633
   148
    EntityBase *curr_ent;
alpar@1633
   149
    /*! \brief Pointer to the best entity. */
alpar@1633
   150
    EntityBase *best_ent;
alpar@1633
   151
    /*! \brief Does initializations before each run. */
alpar@1633
   152
    void init() {
alpar@1633
   153
      SimAnnBase::init();
alpar@1633
   154
      if (best_ent) delete best_ent;
alpar@1633
   155
      best_ent = NULL;
alpar@1633
   156
      curr_ent->randomize();
alpar@1633
   157
    }
alpar@1633
   158
  public:
alpar@1633
   159
    /*! \brief Constructor. */
alpar@1633
   160
    SimAnn() : curr_ent(NULL), best_ent(NULL) {}
alpar@1633
   161
    /*! \brief Destructor. */
alpar@1633
   162
    virtual ~SimAnn() {
alpar@1633
   163
      if (best_ent) delete best_ent;
alpar@1633
   164
    }
alpar@1633
   165
    /*! \brief Step to a neighbouring state. */
alpar@1633
   166
    double mutate() {
alpar@1633
   167
      return curr_ent->mutate();
alpar@1633
   168
    }
alpar@1633
   169
    /*! \brief Reverts the last mutate(). */
alpar@1633
   170
    void revert() {
alpar@1633
   171
      curr_ent->revert();
alpar@1633
   172
    }
alpar@1633
   173
    /*! \brief Saves the current solution as the best one. */
alpar@1633
   174
    void saveAsBest() { 
alpar@1633
   175
      if (best_ent) delete best_ent;
alpar@1633
   176
      best_ent = curr_ent->clone();
alpar@1633
   177
    }
alpar@1633
   178
    /*! \brief Sets the current entity. */
alpar@1633
   179
    void setEntity(EntityBase &_ent) {
alpar@1633
   180
      curr_ent = &_ent;
alpar@1633
   181
    }
alpar@1633
   182
    /*! \brief Returns a copy of the best found entity. */
alpar@1633
   183
    EntityBase* getBestEntity() { return best_ent->clone(); }
alpar@1633
   184
  };
alpar@1633
   185
alpar@1633
   186
  /*! \brief A simple controller for the simulated annealing class. */
alpar@1633
   187
  class SimpleController : public ControllerBase {
alpar@1633
   188
  public:
alpar@1633
   189
    /*! \brief Maximum number of iterations. */
alpar@1633
   190
    long max_iter;
alpar@1633
   191
    /*! \brief Maximum number of iterations which do not improve the
alpar@1633
   192
     *  solution. */
alpar@1633
   193
    long max_no_impr;
alpar@1633
   194
    /*! \brief Temperature. */
alpar@1633
   195
    double temp;
alpar@1633
   196
    /*! \brief Annealing factor. */
alpar@1633
   197
    double ann_fact;
alpar@1633
   198
    /*! \brief Constructor.
alpar@1633
   199
     *  \param _max_iter maximum number of iterations
alpar@1633
   200
     *  \param _max_no_impr maximum number of consecutive iterations which do
alpar@1633
   201
     *         not yield a better solution
alpar@1633
   202
     *  \param _temp initial temperature
alpar@1633
   203
     *  \param _ann_fact annealing factor
alpar@1633
   204
     */
alpar@1633
   205
    SimpleController(long _max_iter = 500000, long _max_no_impr = 20000,
alpar@1633
   206
    double _temp = 1000.0, double _ann_fact = 0.9999) : max_iter(_max_iter),
alpar@1633
   207
      max_no_impr(_max_no_impr), temp(_temp), ann_fact(_ann_fact)
alpar@1633
   208
    {
alpar@1633
   209
      srand48(time(0));
alpar@1633
   210
    }
alpar@1633
   211
    /*! \brief This is called when a neighbouring state gets accepted. */
alpar@1633
   212
    void acceptEvent() {}
alpar@1633
   213
    /*! \brief This is called when the accepted neighbouring state's cost is
alpar@1633
   214
     *  less than the best found one's.
alpar@1633
   215
     */
alpar@1633
   216
    void improveEvent() {}
alpar@1633
   217
    /*! \brief This is called when a neighbouring state gets rejected. */
alpar@1633
   218
    void rejectEvent() {}
alpar@1633
   219
    /*! \brief Decides whether to continue the annealing process or not. Also
alpar@1633
   220
     *  decreases the temperature. */
alpar@1633
   221
    bool next() {
alpar@1633
   222
      temp *= ann_fact;
alpar@1633
   223
      bool quit = (simann->getIter() > max_iter) ||
alpar@1633
   224
        (simann->getIter() - simann->getLastImpr() > max_no_impr);
alpar@1633
   225
      return !quit;
alpar@1633
   226
    }
alpar@1633
   227
    /*! \brief Decides whether to accept the current solution or not. */
alpar@1633
   228
    bool accept() {
alpar@1633
   229
      double cost_diff = simann->getPrevCost() - simann->getCurrCost();
alpar@1633
   230
      return (drand48() <= exp(cost_diff / temp));
alpar@1633
   231
    }
alpar@1633
   232
  };
alpar@1633
   233
alpar@1633
   234
  /*! \brief A controller with preset running time for the simulated annealing
alpar@1633
   235
   *  class.
alpar@1633
   236
   *
alpar@1633
   237
   *  With this controller you can set the running time of the annealing
alpar@1633
   238
   *  process in advance. It works the following way: the controller measures
alpar@1633
   239
   *  a kind of divergence. The divergence is the difference of the average
alpar@1633
   240
   *  cost of the recently found solutions the cost of the best found one. In
alpar@1633
   241
   *  case this divergence is greater than a given threshold, then we decrease
alpar@1633
   242
   *  the annealing factor, that is we cool the system faster. In case the
alpar@1633
   243
   *  divergence is lower than the threshold, then we increase the temperature.
alpar@1633
   244
   *  The threshold is a function of the elapsed time which reaches zero at the
alpar@1633
   245
   *  desired end time.
alpar@1633
   246
   */
alpar@1633
   247
  class AdvancedController : public ControllerBase {
alpar@1633
   248
  private:
alpar@1633
   249
    Timer timer;
alpar@1633
   250
    /*! \param time the elapsed time in seconds */
alpar@1633
   251
    virtual double threshold(double time) {
alpar@1633
   252
      return (-1.0) * start_threshold / end_time * time + start_threshold;
alpar@1633
   253
    }
alpar@1633
   254
  public:
alpar@1633
   255
    double alpha;
alpar@1633
   256
    double beta;
alpar@1633
   257
    double gamma;
alpar@1633
   258
    /*! \brief The time at the end of the algorithm. */
alpar@1633
   259
    double end_time;
alpar@1633
   260
    /*! \brief The time at the start of the algorithm. */
alpar@1633
   261
    double start_time;
alpar@1633
   262
    /*! \brief Starting threshold. */
alpar@1633
   263
    double start_threshold;
alpar@1633
   264
    /*! \brief Average cost of recent solutions. */
alpar@1633
   265
    double avg_cost;
alpar@1633
   266
    /*! \brief Temperature. */
alpar@1633
   267
    double temp;
alpar@1633
   268
    /*! \brief Annealing factor. */
alpar@1633
   269
    double ann_fact;
alpar@1633
   270
    /*! \brief Initial annealing factor. */
alpar@1633
   271
    double init_ann_fact;
alpar@1633
   272
    bool warmup;
alpar@1633
   273
    /*! \brief Constructor.
alpar@1633
   274
     *  \param _end_time running time in seconds
alpar@1633
   275
     *  \param _alpha parameter used to calculate the running average
alpar@1633
   276
     *  \param _beta parameter used to decrease the annealing factor
alpar@1633
   277
     *  \param _gamma parameter used to increase the temperature
alpar@1633
   278
     *  \param _ann_fact initial annealing factor
alpar@1633
   279
     */
alpar@1633
   280
    AdvancedController(double _end_time, double _alpha = 0.2,
alpar@1633
   281
    double _beta = 0.9, double _gamma = 1.6, double _ann_fact = 0.9999) :
alpar@1633
   282
    alpha(_alpha), beta(_beta), gamma(_gamma), end_time(_end_time),
alpar@1633
   283
    ann_fact(_ann_fact), init_ann_fact(_ann_fact), warmup(true)
alpar@1633
   284
    {
alpar@1633
   285
      srand48(time(0));
alpar@1633
   286
    }
alpar@1633
   287
    void init() {
alpar@1633
   288
      avg_cost = simann->getCurrCost();
alpar@1633
   289
    }
alpar@1633
   290
    /*! \brief This is called when a neighbouring state gets accepted. */
alpar@1633
   291
    void acceptEvent() {
alpar@1633
   292
      avg_cost = alpha * simann->getCurrCost() + (1.0 - alpha) * avg_cost;
alpar@1633
   293
      if (warmup) {
alpar@1633
   294
        static int cnt = 0;
alpar@1633
   295
        cnt++;
alpar@1633
   296
        if (cnt >= 100) {
alpar@1633
   297
          // calculate starting threshold and starting temperature
alpar@1633
   298
          start_threshold = 5.0 * fabs(simann->getBestCost() - avg_cost);
alpar@1633
   299
          temp = 10000.0;
alpar@1633
   300
          warmup = false;
alpar@1633
   301
          timer.reset();
alpar@1633
   302
        }
alpar@1633
   303
      }
alpar@1633
   304
    }
alpar@1633
   305
    /*! \brief Decides whether to continue the annealing process or not. */
alpar@1633
   306
    bool next() {
alpar@1633
   307
      if (warmup) {
alpar@1633
   308
        return true;
alpar@1633
   309
      }
alpar@1633
   310
      else {
alpar@1633
   311
        double elapsed_time = timer.getRealTime();
alpar@1633
   312
        if (fabs(avg_cost - simann->getBestCost()) > threshold(elapsed_time)) {
alpar@1633
   313
          // decrease the annealing factor
alpar@1633
   314
          ann_fact *= beta;
alpar@1633
   315
        }
alpar@1633
   316
        else {
alpar@1633
   317
          // increase the temperature
alpar@1633
   318
          temp *= gamma;
alpar@1633
   319
          // reset the annealing factor
alpar@1633
   320
          ann_fact = init_ann_fact;
alpar@1633
   321
        }
alpar@1633
   322
        temp *= ann_fact;
alpar@1633
   323
        return elapsed_time < end_time;
alpar@1633
   324
      }
alpar@1633
   325
    }
alpar@1633
   326
    /*! \brief Decides whether to accept the current solution or not. */
alpar@1633
   327
    bool accept() {
alpar@1633
   328
      if (warmup) {
alpar@1633
   329
        // we accept eveything during the "warm up" phase
alpar@1633
   330
        return true;
alpar@1633
   331
      }
alpar@1633
   332
      else {
alpar@1633
   333
        double cost_diff = simann->getPrevCost() - simann->getCurrCost();
alpar@1633
   334
        if (cost_diff < 0.0) {
alpar@1633
   335
          return (drand48() <= exp(cost_diff / temp));
alpar@1633
   336
        }
alpar@1633
   337
        else {
alpar@1633
   338
          return true;
alpar@1633
   339
        }
alpar@1633
   340
      }
alpar@1633
   341
    }
alpar@1633
   342
  };
alpar@1633
   343
alpar@1633
   344
/// @}
alpar@1633
   345
alpar@1633
   346
}
alpar@1633
   347
alpar@1633
   348
#endif