lemon/tabu_search.h
author deba
Tue, 17 Oct 2006 10:50:57 +0000
changeset 2247 269a0dcee70b
parent 2067 cd414bfbe38b
child 2370 ed6539025f27
permissions -rw-r--r--
Update the Path concept
Concept check for paths

DirPath renamed to Path
The interface updated to the new lemon interface
Make difference between the empty path and the path from one node
Builder interface have not been changed
// I wanted but there was not accordance about it

UPath is removed
It was a buggy implementation, it could not iterate on the
nodes in the right order
Right way to use undirected paths => path of edges in undirected graphs

The tests have been modified to the current implementation
     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* what() const throw() {
   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