|
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 |