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