lemon/tabu_search.h
changeset 2119 4cf25c61ea65
child 2151 38ec4a930c05
equal deleted inserted replaced
-1:000000000000 0:3b7354df4359
       
     1 /* -*- C++ -*-
       
     2  *
       
     3  * This file is a part of LEMON, a generic C++ optimization library
       
     4  *
       
     5  * Copyright (C) 2003-2006
       
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
       
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
       
     8  *
       
     9  * Permission to use, modify and distribute this software is granted
       
    10  * provided that this copyright notice appears in all copies. For
       
    11  * precise terms see the accompanying LICENSE file.
       
    12  *
       
    13  * This software is provided "AS IS" with no warranty of any kind,
       
    14  * express or implied, and with no claim as to its suitability for any
       
    15  * purpose.
       
    16  *
       
    17  */
       
    18 
       
    19 
       
    20 #ifndef LEMON_TABU_SEARCH_H
       
    21 #define LEMON_TABU_SEARCH_H
       
    22 
       
    23 /// \ingroup experimental
       
    24 /// \file
       
    25 /// \brief TabuSearch algorithm.
       
    26 ///
       
    27 /// \author Szabadkai Mark
       
    28 
       
    29 #include <lemon/bits/utility.h>
       
    30 #include <lemon/error.h>
       
    31 #include <lemon/time_measure.h>
       
    32 #include <functional>
       
    33 #include <deque>
       
    34 
       
    35 
       
    36 namespace lemon {
       
    37 
       
    38   /// \brief Default Traits for TabuSearch class.
       
    39   /// 
       
    40   /// This template defines the needed types for the \ref TabuSearch class.
       
    41   /// Is main purpos is to simplify the main class's template interface,
       
    42   /// but it provides the EdgeIt type, passing to the concrete graph wheter
       
    43   /// it is directed or undirected.
       
    44 #ifdef DOXYGEN
       
    45   template< typename GRAPH, typename VALUE, 
       
    46             typename HEIGHTMAP, typename BETTER, bool UNDIR >
       
    47 #else
       
    48   template< typename GRAPH, typename VALUE,
       
    49             typename HEIGHTMAP = typename GRAPH::template NodeMap<VALUE>,
       
    50             typename BETTER = std::less<VALUE>,
       
    51             bool UNDIR = UndirectedTagIndicator<GRAPH>::value >
       
    52 #endif
       
    53   struct TabuSearchDefaultTraits {
       
    54     typedef  VALUE  Value; 
       
    55     typedef  BETTER  Better;
       
    56 
       
    57     typedef  GRAPH  Graph;
       
    58     typedef  typename GRAPH::Node  Node;
       
    59     typedef  HEIGHTMAP  HeightMap;
       
    60 
       
    61     typedef  typename GRAPH::IncEdgeIt  EdgeIt;
       
    62   };
       
    63 
       
    64   template< typename GRAPH, typename VALUE, 
       
    65             typename HEIGHTMAP, typename BETTER >
       
    66   struct TabuSearchDefaultTraits< GRAPH, VALUE, HEIGHTMAP, BETTER, false > {
       
    67     typedef  VALUE  Value;
       
    68     typedef  BETTER  Better;
       
    69 
       
    70     typedef  GRAPH  Graph;
       
    71     typedef  typename GRAPH::Node  Node;
       
    72     typedef  HEIGHTMAP  HeightMap;
       
    73 
       
    74     typedef  typename GRAPH::OutEdgeIt  EdgeIt;
       
    75   };
       
    76 
       
    77 
       
    78 
       
    79   /// \brief Policy hierarchy to controll the search algorithm.
       
    80   ///
       
    81   /// The fallowing template hierarchy offers a clean interface to define own
       
    82   /// policies, and combine existing ones.
       
    83   template< typename TS >
       
    84   struct TabuSearchPolicyConcept {
       
    85     void  target( TS *ts ) {}
       
    86 
       
    87     void  reset()  {}
       
    88     bool  onStep() { return false; }
       
    89     bool  onStick() { return false; }
       
    90     bool  onImprove( const typename TS::Value &best ) { return false; }
       
    91   };
       
    92 
       
    93   template< typename TS >
       
    94   struct YesPolicy {
       
    95     void  target( TS *ts ) {}
       
    96 
       
    97     void  reset()  {}
       
    98     bool  onStep() { return true; }
       
    99     bool  onStick() { return true; }
       
   100     bool  onImprove( const typename TS::Value &best ) { return true; }
       
   101   };
       
   102 
       
   103   template< typename TS >
       
   104   struct NoPolicy : public TabuSearchPolicyConcept<TS> {};
       
   105 
       
   106   /// \brief Some basic methode, how tow Policies can be combined
       
   107   struct PolicyAndCombination {
       
   108     static bool  evaluate( const bool r1, const bool r2 ) {
       
   109       return r1 && r2;
       
   110     }
       
   111   };
       
   112 
       
   113   struct PolicyOrCombination {
       
   114     static bool  evaluate( const bool r1, const bool r2 ) {
       
   115       return r1 || r2;
       
   116     }
       
   117   };
       
   118 
       
   119   /// \brief CombinePolicies
       
   120   ///
       
   121   /// It combines tow policies using the given combination methode (mainly
       
   122   /// some of the basic logical methodes) to create a new one.
       
   123 #ifdef DOXYGEN
       
   124   template< template<typename> class CP1, template<typename> class CP2, 
       
   125             typename COMBINATION >
       
   126 #else
       
   127   template< template<typename> class CP1, template<typename> class CP2,
       
   128             typename COMBINATION = PolicyAndCombination >
       
   129 #endif
       
   130   struct CombinePolicies {
       
   131     template< typename TS >
       
   132     struct Policy {
       
   133       typedef CP1<TS>  Policy1;
       
   134       typedef CP2<TS>  Policy2;
       
   135       
       
   136       Policy1  policy1;
       
   137       Policy2  policy2;
       
   138 
       
   139       inline Policy() : policy1(), policy2() {}
       
   140       inline Policy( const Policy1 &cp1, const Policy2 &cp2 ) 
       
   141         : policy1(cp1), policy2(cp2) {}
       
   142 
       
   143       void  target( TS *ts ) {
       
   144         policy1.target(ts), policy2.target(ts);
       
   145       };
       
   146 
       
   147       void  reset() {
       
   148         policy1.reset(), policy2.reset();
       
   149       }
       
   150 
       
   151       bool  onStep() {
       
   152         return cmb.evaluate( policy1.onStep(), policy2.onStep() );
       
   153       }
       
   154 
       
   155       bool  onStick() {
       
   156         return cmb.evaluate( policy1.onStick(), policy2.onStick() );
       
   157       }
       
   158 
       
   159       bool  onImprove( const typename TS::Value &best ) {
       
   160         return cmb.evaluate( policy1.onImprove(best), 
       
   161                              policy2.onImprove(best) );
       
   162       }
       
   163 
       
   164     private:
       
   165       COMBINATION cmb;
       
   166     };
       
   167   };
       
   168 
       
   169 
       
   170   /// \brief IterationPolicy limits the number of iterations and the
       
   171   /// number of iterations without improvement
       
   172   template< typename TS >
       
   173   struct IterationPolicy {
       
   174     IterationPolicy() : _it_lim(100000), _noimpr_it_lim(5000) {}
       
   175     IterationPolicy( const long int itl, const long int noimpritl )
       
   176       : _it_lim(itl), _noimpr_it_lim(noimpritl)
       
   177     {}
       
   178 
       
   179     void  target( TS *ts ) {}
       
   180 
       
   181     void  reset() {
       
   182       _it = _noimpr_it = 0;
       
   183     }
       
   184 
       
   185     bool  onStep() {
       
   186       ++_it; ++_noimpr_it;
       
   187       return (_it <= _it_lim) && (_noimpr_it <= _noimpr_it_lim);
       
   188     }
       
   189 		
       
   190     bool  onStick() {
       
   191       return false;
       
   192     }
       
   193 
       
   194     bool  onImprove( const typename TS::Value &best ) {
       
   195       _noimpr_it = 0;
       
   196       return true;
       
   197     }
       
   198 
       
   199     long int  iterationLimit() const {
       
   200       return _it_lim;
       
   201     }
       
   202 
       
   203     void  iterationLimit( const long int itl ) {
       
   204       _it_lim = itl;
       
   205     }
       
   206 
       
   207     long int  noImprovementIterationLimit() const {
       
   208       return _noimpr_it_lim;
       
   209     }
       
   210 
       
   211     void  noImprovementIterationLimit( const long int noimpritl ) {
       
   212       _noimpr_it_lim = noimpritl;
       
   213     }
       
   214 
       
   215   private:
       
   216     long int  _it_lim, _noimpr_it_lim;
       
   217     long int  _it, _noimpr_it;
       
   218   };
       
   219 
       
   220   /// \brief HeightPolicy stops the search when a given height is reached or
       
   221   /// exceeds
       
   222   template< typename TS >
       
   223   struct HeightPolicy {
       
   224     typedef typename TS::Value  Value;
       
   225 
       
   226     HeightPolicy() : _height_lim(), _found(false) {}
       
   227     HeightPolicy( const Value &hl ) : _height_lim(hl), _found(false) {}
       
   228 
       
   229     void  target( TS *ts ) {}
       
   230 
       
   231     void  reset() {
       
   232       _found = false;
       
   233     }
       
   234 
       
   235     bool  onStep() {
       
   236       return !_found;
       
   237     }
       
   238 
       
   239     bool  onStick() {
       
   240       return false;
       
   241     }
       
   242 
       
   243     bool  onImprove( const Value &best ) {
       
   244       typename TS::Better  better;
       
   245       _found = better(best, _height_lim) || (best == _height_lim);
       
   246       return !_found;
       
   247     }
       
   248 
       
   249     Value  heightLimi() const {
       
   250       return _height_lim;
       
   251     }
       
   252 
       
   253     void  heightLimi( const Value &hl ) {
       
   254       _height_lim = hl;
       
   255     }
       
   256 
       
   257   private:
       
   258     Value  _height_lim;
       
   259     bool  _found;
       
   260   };
       
   261 
       
   262   /// \brief TimePolicy limits the time for searching.
       
   263   template< typename TS >
       
   264   struct TimePolicy {
       
   265     TimePolicy() : _time_lim(60.0), _timeisup(false) {}
       
   266     TimePolicy( const double tl ) : _time_lim(tl), _timeisup(false) {}
       
   267 
       
   268     void  target( TS *ts ) {}
       
   269 
       
   270     void  reset() {
       
   271       _timeisup = false;
       
   272       _t.reset();
       
   273     }
       
   274 
       
   275     bool  onStep() {
       
   276       update();
       
   277       return !_timeisup;
       
   278     }
       
   279 
       
   280     bool  onStick() {
       
   281       return false;
       
   282     }
       
   283 
       
   284     bool  onImprove( const typename TS::Value &best ) {
       
   285       update();
       
   286       return !_timeisup;
       
   287     }
       
   288 
       
   289     double timeLimit() const {
       
   290       return _time_lim;
       
   291     }
       
   292 
       
   293     void  setTimeLimit( const double tl ) {
       
   294       _time_lim = tl;
       
   295       update();
       
   296     }
       
   297 
       
   298   private:
       
   299     lemon::Timer  _t;
       
   300     double  _time_lim;
       
   301     bool  _timeisup;
       
   302 
       
   303     inline void  update() {
       
   304       _timeisup = _t.realTime() > _time_lim;
       
   305     }
       
   306   };
       
   307 
       
   308 
       
   309 
       
   310   /// \brief TabuSearch main class
       
   311   ///
       
   312   /// This class offers the implementation of tabu-search algorithm. The
       
   313   /// tabu-serach is a local-search. It starts from a specified point of the
       
   314   /// problem's graph representation, and in every step it goes to the localy
       
   315   /// best next Node except those in tabu set. The maximum size of this tabu
       
   316   /// set defines how many Node will be remembered. The best Node ever found
       
   317   /// will also stored, so we wont lose it, even is the search continues.
       
   318   /// The class can be used on any kind of Graph and with any kind of Value
       
   319   /// with a total-settlement on it.
       
   320   ///
       
   321   /// \param _Graph The graph type the algorithm runs on.
       
   322   /// \param _Value The values' type associated to the nodes.
       
   323   /// \param _Policy Controlls the search. Determinates when to stop, or how
       
   324   /// manage stuck search. Default value is \ref IterationPolicy .
       
   325   /// \param _Traits Collection of needed types. Default value is
       
   326   /// \ref TabuSearchDefaultTraits .
       
   327   ///
       
   328   /// \author Szabadkai Mark
       
   329 #ifdef DOXYGEN
       
   330   template< typename GRAPH, typename VALUE, template<typename> class POLICY, typename TRAITS >
       
   331 #else
       
   332   template< typename GRAPH, typename VALUE,
       
   333             template<typename> class POLICY = IterationPolicy,
       
   334             typename TRAITS = TabuSearchDefaultTraits<GRAPH, VALUE> >
       
   335 #endif
       
   336   class TabuSearch
       
   337   {
       
   338   public:
       
   339 
       
   340     /// \brief Thrown by setting the size of the tabu-set and the given size
       
   341     /// is less than 2.
       
   342     class BadParameterError : public lemon::LogicError {
       
   343     public:
       
   344       virtual const char* exceptionName() const {
       
   345         return "lemon::TabuSearch::BadParameterError";
       
   346       }
       
   347     };
       
   348 
       
   349     ///Public types
       
   350     typedef  TabuSearch<GRAPH,VALUE,POLICY,TRAITS>  SelfType;
       
   351 
       
   352     typedef  typename TRAITS::Graph  Graph;
       
   353     typedef  typename TRAITS::Node  Node;
       
   354     typedef  typename TRAITS::Value  Value;
       
   355     typedef  typename TRAITS::HeightMap  HeightMap;
       
   356     typedef  typename TRAITS::Better  Better;
       
   357     typedef  typename std::deque< Node >::const_iterator  TabuIterator;
       
   358 
       
   359     typedef  POLICY<SelfType>  Policy;
       
   360 
       
   361   protected:
       
   362     typedef  typename TRAITS::EdgeIt  EdgeIt;
       
   363 
       
   364     const Graph  &gr;
       
   365     const HeightMap  &height;
       
   366     /// The tabu set. Teh current node is the first
       
   367     std::deque< Node >  tabu;
       
   368     /// Maximal tabu size
       
   369     unsigned int  mts;
       
   370     /// The best Node found
       
   371     Node  b;
       
   372 
       
   373     Better  better;
       
   374     Policy  pol;
       
   375 
       
   376   public:
       
   377     /// \brief Constructor
       
   378     ///
       
   379     /// \param graph the graph the algorithm will run on.
       
   380     /// \param hm the height map used by the algorithm.
       
   381     /// \param tabusz the maximal size of the tabu set. Default value is 3
       
   382     /// \param p the Policy controlling the search.
       
   383     TabuSearch( const Graph &graph, const HeightMap &hm, 
       
   384                 const int tabusz = 3, Policy p = Policy() )
       
   385       : gr(graph), height(hm), mts(tabusz), pol(p)
       
   386     {
       
   387       pol.target(this);
       
   388     }
       
   389 
       
   390     /// \brief Destructor
       
   391     ~TabuSearch() {
       
   392       pol.target(NULL);
       
   393     }
       
   394 
       
   395     /// Set/Get the size of the tabu set
       
   396     void  tabuSize( const unsigned int size )
       
   397     {
       
   398       if( size < 2 )
       
   399       throw BadParameterError( "Tabu size must be at least 2!" );
       
   400       mts = size;
       
   401       while( mts < tabu.size() )
       
   402       tabu.pop_back();
       
   403     }
       
   404 
       
   405     unsigned int  tabuSize() const {
       
   406       return mts;
       
   407     }
       
   408 
       
   409     /// Set/Get Policy
       
   410     void  policy( Policy p ) {
       
   411       pol.target(NULL);
       
   412       pol = p;
       
   413       pol.target(this);
       
   414     }
       
   415 		
       
   416     Policy& policy()  {
       
   417       return pol;
       
   418     }
       
   419 
       
   420     /// \name Execution control
       
   421     /// The simplest way to execute the algorithm is to use the member
       
   422     /// functions called \c run( 'startnode' ).
       
   423     ///@{
       
   424 
       
   425     /// \brief Initializes the internal data.
       
   426     ///
       
   427     /// \param startn The start node where the search begins.
       
   428     void  init( const Node startn ) {
       
   429       tabu.clear();
       
   430       tabu.push_front( startn );
       
   431       b = startn;
       
   432       pol.reset();
       
   433     }
       
   434 
       
   435     /// \brief Does one iteration
       
   436     ///
       
   437     /// If the Policy allows it searches for the best next node, then steps
       
   438     /// onto it.
       
   439     /// \return %False if one Policy condition wants to stop the search.
       
   440     bool  step()
       
   441     {
       
   442       ///Request premmision from ControllPolicy
       
   443       if( !pol.onStep() )
       
   444       return false;
       
   445 	
       
   446       ///Find the best next potential node
       
   447       Node n; bool found = false;
       
   448       for( EdgeIt e(gr,tabu[0]); e != INVALID; ++e )
       
   449       {
       
   450         Node m = (gr.source(e) == tabu[0]) ? gr.target(e) : gr.source(e);
       
   451         bool wrong = false;
       
   452         for( int i = 1; i != (signed int)tabu.size(); ++i )
       
   453           if( m == tabu[i] ) {
       
   454             wrong = true;
       
   455             break;
       
   456           }
       
   457         if( wrong )
       
   458           continue;
       
   459 
       
   460         if( !found ) {
       
   461           n = m;
       
   462           found = true;
       
   463         } else
       
   464           if( better(height[m], height[n]) ) {
       
   465             n = m;
       
   466           }
       
   467       }
       
   468 
       
   469       ///Handle stuck search
       
   470       if( !found ) {
       
   471         return pol.onStick();
       
   472       }
       
   473 
       
   474       ///Move on...
       
   475       tabu.push_front(n);
       
   476       while( mts < tabu.size() ) {
       
   477         tabu.pop_back();
       
   478       }
       
   479       if( better(height[n], height[b]) ) {
       
   480         b = n;
       
   481         if( !pol.onImprove(height[b]) )
       
   482         return false;
       
   483       }
       
   484 
       
   485       return true;
       
   486     }
       
   487 
       
   488     /// \brief Runs a search while the Policy stops it.
       
   489     ///
       
   490     /// \param startn The start node where the search begins.
       
   491     inline void  run( const Node startn ) {
       
   492       std::cin.unsetf( std::ios_base::skipws );
       
   493       char c;
       
   494       init( startn );
       
   495       while( step() )
       
   496       std::cin >> c;
       
   497       std::cin.setf( std::ios_base::skipws );
       
   498     }
       
   499 
       
   500     ///@}
       
   501 
       
   502     /// \name Query Functions
       
   503     /// The result of the TabuSearch algorithm can be obtained using these
       
   504     /// functions.\n
       
   505     ///@{
       
   506 
       
   507     /// \brief The node, the search is standing on.
       
   508     inline Node  current() const {
       
   509       return tabu[0];
       
   510     }
       
   511 
       
   512     /// \brief The best node found until now.
       
   513     inline Node  best() const {
       
   514       return b;
       
   515     }
       
   516 
       
   517     /// \brief Beginning to iterate on the current tabu set.
       
   518     inline TabuIterator  tabu_begin() const {
       
   519       return tabu.begin();
       
   520     }
       
   521 
       
   522     /// \brief Ending to iterate on the current tabu set.
       
   523     inline TabuIterator  tabu_end() const {
       
   524       return tabu.end();
       
   525     }
       
   526 
       
   527     ///@}
       
   528   };
       
   529 }
       
   530 #endif