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