1 /*
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2006-2012, Mirko Maischberger <mirko.maischberger@gmail.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33 #include <iostream>
34
35 #ifndef METS_ABSTRACT_SEARCH_HH_
36 #define METS_ABSTRACT_SEARCH_HH_
37
38 namespace mets {
39
40 /// @defgroup common Common components
41 /// @{
42
43 /// @brief The solution recorder is used by search algorithm, at the
44 /// end of each iteration, to record the best seen solution.
45 ///
46 /// The concept of best is externalized so that you can record the
47 /// best ever solution met or the best solution that matches some
48 /// other criteria (e.g. feasibility constraints relaxed in the
49 /// feasible_solution implementation of the cost function).
50 ///
51 class solution_recorder {
52 public:
53 /// @brief Default ctor.
solution_recorder()54 solution_recorder() {}
55 /// @brief Unimplemented copy ctor.
56 solution_recorder(const solution_recorder&);
57 /// @brief Unimplemented assignment operator.
58 solution_recorder& operator=(const solution_recorder&);
59
60 /// @brief A virtual dtor.
61 virtual
62 ~solution_recorder();
63
64 /// @brief Accept is called at the end of each iteration for an
65 /// opportunity to record the best move ever.
66 ///
67 /// (this is a chain of responsibility)
68 ///
69 virtual bool
70 accept(const feasible_solution& sol) = 0;
71
72 virtual gol_type
73 best_cost() const = 0;
74 };
75
76 /// @brief An abstract search.
77 ///
78 /// @see mets::tabu_search, mets::simulated_annealing, mets::local_search
79 template<typename move_manager_type>
80 class abstract_search : public subject< abstract_search<move_manager_type> >
81 {
82 public:
83 /// @brief Set some common values needed for neighborhood based
84 /// metaheuristics.
85 ///
86 /// @param working The starting point solution (this will be modified
87 /// during search as the working solution)
88 ///
89 /// @param recorder A solution recorder instance used to record
90 /// the best solution found
91 ///
92 /// @param moveman A problem specific implementation of the
93 /// move_manager_type used to generate the neighborhood.
94 ///
abstract_search(feasible_solution & working,solution_recorder & recorder,move_manager_type & moveman)95 abstract_search(feasible_solution& working,
96 solution_recorder& recorder,
97 move_manager_type& moveman)
98 : subject<abstract_search<move_manager_type> >(),
99 solution_recorder_m(recorder),
100 working_solution_m(working),
101 moves_m(moveman),
102 current_move_m(),
103 step_m()
104 { }
105
106 /// purposely not implemented (see Effective C++)
107 abstract_search(const abstract_search<move_manager_type>&);
108 /// purposely not implemented (see Effective C++)
109 abstract_search& operator==(const abstract_search<move_manager_type>&);
110
111 /// @brief Virtual destructor.
112 virtual
~abstract_search()113 ~abstract_search()
114 { };
115
116 enum {
117 /// @brief We just made a move.
118 MOVE_MADE = 0,
119 /// @brief Our solution_recorder_chain object reported an improvement
120 IMPROVEMENT_MADE,
121 /// @brief We are about to start a new iteration
122 ITERATION_BEGIN,
123 /// @brief We have done the iteration
124 ITERATION_END,
125 /// @brief Placeholer for next values
126 LAST
127 };
128
129 /// @brief This method starts the search.
130 ///
131 /// Remember that this is a minimization.
132 ///
133 /// An exception mets::no_moves_error can be risen when no move is
134 /// possible.
135 virtual void
136 search() = 0;
137
138 /// @brief The solution recorder instance.
139 const solution_recorder&
recorder() const140 recorder() const
141 { return solution_recorder_m; };
142
143 /// @brief The current working solution.
144 const feasible_solution&
working() const145 working() const
146 { return working_solution_m; }
147
148 feasible_solution&
working()149 working()
150 { return working_solution_m; }
151
152 /// @brief The last move made
153 const move&
current_move() const154 current_move() const
155 { return **current_move_m; }
156
157 /// @brief The last move made
158 move&
current_move()159 current_move()
160 { return **current_move_m; }
161
162 /// @brief The move manager used by this search
163 const move_manager_type&
move_manager() const164 move_manager() const
165 { return moves_m; }
166
167 /// @brief The move manager used by this search
168 move_manager_type&
move_manager()169 move_manager()
170 { return moves_m; }
171
172 /// @brief The current step of the algorithm (to be used by the
173 /// observers).
174 ///
175 /// When you implement a new type of search you should set step_m
176 /// protected variable to the status of the algorithm
177 /// (0 = "MOVE_MADE", 1 = "IMPROVEMENT_MADE", etc.).
178 int
step() const179 step() const
180 { return step_m; }
181
182 protected:
183 solution_recorder& solution_recorder_m;
184 feasible_solution& working_solution_m;
185 move_manager_type& moves_m;
186 typename move_manager_type::iterator current_move_m;
187 int step_m;
188 };
189
190 /// @}
191
192 /// @defgroup common Common components
193 /// @{
194
195 /// @brief The best ever solution recorder can be used as a simple
196 /// solution recorder that just records the best copyable solution
197 /// found during its lifetime.
198 ///
199 class best_ever_solution : public solution_recorder
200 {
201 public:
202 /// @brief The mets::evaluable_solution will be stored as a
203 /// reference: please provide an instance that is not
204 /// modified/needed elsewhere.
205 ///
206 /// @param best The instance used to store the best solution found
207 /// (will be modified).
best_ever_solution(evaluable_solution & best)208 best_ever_solution(evaluable_solution& best) :
209 solution_recorder(),
210 best_ever_m(best)
211 { }
212
213 /// @brief Unimplemented default ctor.
214 best_ever_solution();
215 /// @brief Unimplemented copy ctor.
216 best_ever_solution(const best_ever_solution&);
217 /// @brief Unimplemented assignment operator.
218 best_ever_solution& operator=(const best_ever_solution&);
219
220 /// @brief Accept is called at the end of each iteration for an
221 /// opportunity to record the best solution found during the
222 /// search.
223 bool accept(const feasible_solution& sol);
224
225 /// @brief Returns the best solution found since the beginning.
best_seen() const226 const evaluable_solution& best_seen() const
227 { return best_ever_m; }
228
229 /// @brief Best cost seen.
best_cost() const230 gol_type best_cost() const
231 { return best_ever_m.cost_function(); }
232 protected:
233 /// @brief Records the best solution
234 evaluable_solution& best_ever_m;
235 };
236
237 /// @brief An object that is called back during the search progress.
238 template<typename move_manager_type>
239 class search_listener : public observer<abstract_search<move_manager_type> >
240 {
241 public:
242 using search_type = abstract_search<move_manager_type>;
243 /// @brief A new observer (listener) of a search process, remember
244 /// to attach the created object to the search process to be
245 /// observed (mets::search_type::attach())
246 explicit
search_listener()247 search_listener() : observer<search_type>()
248 { }
249
250 /// purposely not implemented (see Effective C++)
251 search_listener(const search_listener<search_type>& other);
252 search_listener<search_type>&
253 operator=(const search_listener<search_type>& other);
254
255 /// @brief Virtual destructor
256 virtual
~search_listener()257 ~search_listener()
258 { }
259
260 /// @brief This is the callback method called by searches
261 /// when a move, an improvement or something else happens
262 virtual void
263 update(search_type* algorithm) = 0;
264 };
265
266
267 template<typename neighborhood_t>
268 struct iteration_logger : public mets::search_listener<neighborhood_t>
269 {
270 explicit
iteration_loggermets::iteration_logger271 iteration_logger(std::ostream& o)
272 : mets::search_listener<neighborhood_t>(),
273 iteration(0),
274 os(o)
275 { }
276
277 void
updatemets::iteration_logger278 update(mets::abstract_search<neighborhood_t>* as)
279 {
280 const mets::feasible_solution& p = as->working();
281 if(as->step() == mets::abstract_search<neighborhood_t>::MOVE_MADE)
282 {
283 os << iteration++ << "\t"
284 << static_cast<const mets::evaluable_solution&>(p).cost_function()
285 << "\n";
286 }
287 }
288
289 protected:
290 int iteration;
291 std::ostream& os;
292 };
293
294 template<typename neighborhood_t>
295 struct improvement_logger : public mets::search_listener<neighborhood_t>
296 {
297 explicit
improvement_loggermets::improvement_logger298 improvement_logger(std::ostream& o, gol_type epsilon = 1e-7)
299 : mets::search_listener<neighborhood_t>(),
300 iteration_m(0),
301 best_m(std::numeric_limits<double>::max()),
302 os_m(o),
303 epsilon_m(epsilon)
304 { }
305
306 void
updatemets::improvement_logger307 update(mets::abstract_search<neighborhood_t>* as)
308 {
309 const mets::feasible_solution& p = as->working();
310
311 if(as->step() == mets::abstract_search<neighborhood_t>::MOVE_MADE)
312 {
313 iteration_m++;
314 double val = static_cast<const mets::evaluable_solution&>(p)
315 .cost_function();
316 if(val < best_m - epsilon_m)
317 {
318 best_m = val;
319 os_m << iteration_m << "\t"
320 << best_m
321 << " (*)\n";
322 }
323 }
324 }
325
326 protected:
327 int iteration_m;
328 double best_m;
329 std::ostream& os_m;
330 gol_type epsilon_m;
331 };
332
333 /// @}
334
335
336 }
337
~solution_recorder()338 inline mets::solution_recorder::~solution_recorder()
339 { }
340
341 inline bool
accept(const mets::feasible_solution & sol)342 mets::best_ever_solution::accept(const mets::feasible_solution& sol)
343 {
344 const evaluable_solution& s = dynamic_cast<const mets::evaluable_solution&>(sol);
345 if(s.cost_function() < best_ever_m.cost_function())
346 {
347 best_ever_m.copy_from(s);
348 return true;
349 }
350 return false;
351 }
352
353 #endif
354