COIN-OR::LEMON - Graph Library

source: lemon-0.x/lemon/simann.h @ 1918:09415ae11103

Last change on this file since 1918:09415ae11103 was 1918:09415ae11103, checked in by Akos Ladanyi, 18 years ago

more doc

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