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