#ifndef LEMON_SIMANN_H #define LEMON_SIMANN_H #include #include #include namespace lemon { const double INFTY = 1e24; class SimAnnBase { public: class Controller; private: Controller *controller; protected: double curr_cost; double best_cost; double prev_cost; double prev_prev_cost; virtual void mutate() = 0; virtual void revert() = 0; virtual void saveAsBest() = 0; public: SimAnnBase() { best_cost = prev_cost = prev_prev_cost = INFTY; } void setController(Controller &_controller) { controller = &_controller; controller->setBase(this); } double getCurrCost() const { return curr_cost; } double getPrevCost() const { return prev_cost; } double getBestCost() const { return best_cost; } void run() { controller->init(); do { mutate(); if (controller->accept()) { controller->acceptEvent(); if (curr_cost < best_cost) { saveAsBest(); controller->improveEvent(); } } else { revert(); controller->rejectEvent(); } } while (controller->next()); } /*! \brief A base class for controllers. */ class Controller { public: SimAnnBase *base; virtual void init() {} /*! \brief This is called when a neighbouring state gets accepted. */ virtual void acceptEvent() {} /*! \brief This is called when the accepted neighbouring state's cost is * less than the best found one's. */ virtual void improveEvent() {} /*! \brief This is called when a neighbouring state gets rejected. */ virtual void rejectEvent() {} virtual void setBase(SimAnnBase *_base) { base = _base; } /*! */ virtual bool next() = 0; /*! */ virtual bool accept() = 0; }; }; template class SimAnn : public SimAnnBase { private: E *curr_ent; E *best_ent; public: SimAnn() : SimAnnBase() {} void setEntity(E &ent) { curr_ent = new E(ent); best_ent = new E(ent); curr_cost = curr_ent->getCost(); } E getBestEntity() { return *best_ent; } void mutate() { prev_prev_cost = prev_cost; prev_cost = curr_cost; curr_ent->mutate(); curr_cost = curr_ent->getCost(); } void revert() { curr_ent->revert(); curr_cost = prev_cost; prev_cost = prev_prev_cost; } void saveAsBest() { delete(best_ent); best_ent = new E(*curr_ent); best_cost = curr_cost; } }; class EntitySkeleton { public: /*! \return the cost of the entity */ double getCost() { return 0.0; } /*! \brief Makes a minor change to the entity. */ void mutate() {} /*! \brief Restores the entity to its previous state i.e. reverts the * effects of the last mutate. */ void revert() {} }; /*! \brief A simple controller for the simulated annealing class. * \todo Find a way to set the various parameters. */ class SimpleController : public SimAnnBase::Controller { public: long iter, last_impr, max_iter, max_no_impr; double temp, ann_fact; /*! \param _max_iter maximum number of iterations * \param _max_no_impr maximum number of consecutive iterations which do * not yield a better solution * \param _temp initial temperature * \param _ann_fact annealing factor */ SimpleController(long _max_iter = 500000, long _max_no_impr = 20000, double _temp = 1000.0, double _ann_fact = 0.9999) : iter(0), last_impr(0), max_iter(_max_iter), max_no_impr(_max_no_impr), temp(_temp), ann_fact(_ann_fact) {} void acceptEvent() { iter++; } void improveEvent() { last_impr = iter; } void rejectEvent() { iter++; } bool next() { temp *= ann_fact; bool quit = (iter > max_iter) || (iter - last_impr > max_no_impr); return !quit; } bool accept() { double cost_diff = base->getPrevCost() - base->getCurrCost(); if (cost_diff < 0.0) { bool ret = drand48() <= exp(cost_diff / temp); return ret; } else { return true; } } }; /*! \brief A controller with preset running time for the simulated annealing * class. * \todo Find a better name. */ class AdvancedController : public SimAnnBase::Controller { private: Timer timer; /*! \param time the elapsed time in seconds */ virtual double threshold(double time) { // 1 / log(x) /* static double xm = 5.0 / end_time; static double ym = start_threshold / (1 / log(1.2) - 1 / log(5.0 + 1.2)); return ym * (1 / log(xm * time + 1.2) - 1 / log(5.0 + 1.2)); */ return (-1.0) * start_threshold / end_time * time + start_threshold; } public: double alpha, beta, gamma; double end_time, start_time; double start_threshold; double avg_cost; double temp, ann_fact; bool warmup; /*! \param _end_time running time in seconds * \param _alpha parameter used to calculate the running average * \param _beta parameter used to decrease the annealing factor * \param _gamma parameter used to increase the temperature */ AdvancedController(double _end_time, double _alpha = 0.2, double _beta = 0.9, double _gamma = 1.6) : alpha(_alpha), beta(_beta), gamma(_gamma), end_time(_end_time), ann_fact(0.99999999), warmup(true) {} void init() { avg_cost = base->getCurrCost(); } void acceptEvent() { avg_cost = alpha * base->getCurrCost() + (1.0 - alpha) * avg_cost; if (warmup) { static int cnt = 0; cnt++; if (cnt >= 100) { // calculate starting threshold and starting temperature start_threshold = 5.0 * fabs(base->getBestCost() - avg_cost); //temp = max_cost_diff / log(0.5); temp = 10000.0; warmup = false; timer.reset(); } } } void improveEvent() { } void rejectEvent() { } bool next() { if (warmup) { return true; } else { double elapsed_time = timer.getRealTime(); if (fabs(avg_cost - base->getBestCost()) > threshold(elapsed_time)) { // decrease the annealing factor ann_fact *= beta; } else { // increase the temperature temp *= gamma; ann_fact = 0.99999999; } temp *= ann_fact; return elapsed_time < end_time; } } bool accept() { if (warmup) { // we accept eveything during the "warm up" phase return true; } else { double cost_diff = base->getPrevCost() - base->getCurrCost(); if (cost_diff < 0.0) { return (drand48() <= exp(cost_diff / temp)); } else { return true; } } } }; } #endif