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