Generated on Mon May 10 06:46:44 2010 for Gecode by doxygen 1.6.3

path.hh

Go to the documentation of this file.
00001 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
00002 /*
00003  *  Main authors:
00004  *     Christian Schulte <schulte@gecode.org>
00005  *
00006  *  Copyright:
00007  *     Christian Schulte, 2003
00008  *
00009  *  Last modified:
00010  *     $Date: 2009-11-25 17:27:09 +0100 (Wed, 25 Nov 2009) $ by $Author: schulte $
00011  *     $Revision: 10126 $
00012  *
00013  *  This file is part of Gecode, the generic constraint
00014  *  development environment:
00015  *     http://www.gecode.org
00016  *
00017  *  Permission is hereby granted, free of charge, to any person obtaining
00018  *  a copy of this software and associated documentation files (the
00019  *  "Software"), to deal in the Software without restriction, including
00020  *  without limitation the rights to use, copy, modify, merge, publish,
00021  *  distribute, sublicense, and/or sell copies of the Software, and to
00022  *  permit persons to whom the Software is furnished to do so, subject to
00023  *  the following conditions:
00024  *
00025  *  The above copyright notice and this permission notice shall be
00026  *  included in all copies or substantial portions of the Software.
00027  *
00028  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00029  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00030  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00031  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00032  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00033  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00034  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00035  *
00036  */
00037 
00038 #ifndef __GECODE_SEARCH_PARALLEL_PATH_HH__
00039 #define __GECODE_SEARCH_PARALLEL_PATH_HH__
00040 
00041 #include <gecode/search.hh>
00042 
00043 namespace Gecode { namespace Search { namespace Parallel {
00044 
00058   class Path {
00059   public:
00061     class Node {
00062     protected:
00064       Space* _space;
00066       unsigned int _alt;
00068       unsigned int _alt_max;
00070       const Choice* _choice;
00071     public:
00073       Node(void);
00075       Node(Space* s, Space* c);
00076       
00078       Space* space(void) const;
00080       void space(Space* s);
00081       
00083       const Choice* choice(void) const;
00084       
00086       unsigned int alt(void) const;
00088       bool rightmost(void) const;
00090       bool work(void) const;
00092       void next(void);
00094       unsigned int steal(void);
00095       
00097       void dispose(void);
00098     };
00099   protected:
00101     Support::DynamicStack<Node,Heap> ds;
00103     unsigned int n_work;
00104   public:
00106     Path(void);
00108     const Choice* push(Worker& stat, Space* s, Space* c);
00110     bool next(Worker& s);
00112     Node& top(void) const;
00114     bool empty(void) const;
00116     int lc(void) const;
00118     void unwind(int l);
00120     void commit(Space* s, int i) const;
00122     Space* recompute(unsigned int& d, unsigned int a_d, Worker& s);
00124     Space* recompute(unsigned int& d, unsigned int a_d, Worker& s,
00125                      const Space* best, int& mark);
00127     int entries(void) const;
00129     size_t size(void) const;
00131     void reset(void);
00133     bool steal(void) const;
00135     Space* steal(Worker& stat, unsigned long int& d);
00136   };
00137 
00138 
00139   /*
00140    * Node for recomputation
00141    *
00142    */
00143   forceinline
00144   Path::Node::Node(void) {}
00145 
00146   forceinline
00147   Path::Node::Node(Space* s, Space* c)
00148     : _space(c), _alt(0), _choice(s->choice()) {
00149     _alt_max = _choice->alternatives()-1;
00150   }
00151 
00152   forceinline Space*
00153   Path::Node::space(void) const {
00154     return _space;
00155   }
00156   forceinline void
00157   Path::Node::space(Space* s) {
00158     _space = s;
00159   }
00160 
00161   forceinline unsigned int
00162   Path::Node::alt(void) const {
00163     return _alt;
00164   }
00165   forceinline bool
00166   Path::Node::rightmost(void) const {
00167     return _alt == _alt_max;
00168   }
00169   forceinline bool
00170   Path::Node::work(void) const {
00171     return _alt != _alt_max;
00172   }
00173   forceinline void
00174   Path::Node::next(void) {
00175     _alt++;
00176   }
00177   forceinline unsigned int
00178   Path::Node::steal(void) {
00179     assert(work());
00180     return _alt_max--;
00181   }
00182 
00183   forceinline const Choice*
00184   Path::Node::choice(void) const {
00185     return _choice;
00186   }
00187 
00188   forceinline void
00189   Path::Node::dispose(void) {
00190     delete _space;
00191     delete _choice;
00192   }
00193 
00194 
00195 
00196   /*
00197    * Depth-first stack with recomputation
00198    *
00199    */
00200 
00201   forceinline
00202   Path::Path(void) : ds(heap), n_work(0) {}
00203 
00204   forceinline const Choice*
00205   Path::push(Worker& stat, Space* s, Space* c) {
00206     Node sn(s,c);
00207     if (sn.work())
00208       n_work++;
00209     ds.push(sn);
00210     stat.stack_depth(static_cast<unsigned long int>(ds.entries()));
00211     return sn.choice();
00212   }
00213 
00214   forceinline bool
00215   Path::next(Worker& stat) {
00216     // Generate path for next node and return whether node exists.
00217     while (!ds.empty())
00218       if (ds.top().rightmost()) {
00219         stat.pop(ds.top().space(),ds.top().choice());
00220         ds.pop().dispose();
00221       } else {
00222         assert(ds.top().work());
00223         ds.top().next();
00224         if (!ds.top().work())
00225           n_work--;
00226         return true;
00227       }
00228     return false;
00229   }
00230 
00231   forceinline Path::Node&
00232   Path::top(void) const {
00233     assert(!ds.empty());
00234     return ds.top();
00235   }
00236 
00237   forceinline bool
00238   Path::empty(void) const {
00239     return ds.empty();
00240   }
00241 
00242   forceinline void
00243   Path::commit(Space* s, int i) const {
00244     const Node& n = ds[i];
00245     s->commit(*n.choice(),n.alt());
00246   }
00247 
00248   forceinline int
00249   Path::lc(void) const {
00250     int l = ds.entries()-1;
00251     while (ds[l].space() == NULL)
00252       l--;
00253     return l;
00254   }
00255 
00256   forceinline int
00257   Path::entries(void) const {
00258     return ds.entries();
00259   }
00260 
00261   forceinline size_t
00262   Path::size(void) const {
00263     return ds.size();
00264   }
00265 
00266   forceinline void
00267   Path::unwind(int l) {
00268     assert((ds[l].space() == NULL) || ds[l].space()->failed());
00269     int n = ds.entries();
00270     for (int i=l; i<n; i++) {
00271       if (ds.top().work())
00272         n_work--;
00273       ds.pop().dispose();
00274     }
00275     assert(ds.entries() == l);
00276   }
00277 
00278   forceinline void
00279   Path::reset(void) {
00280     n_work = 0;
00281     while (!ds.empty())
00282       ds.pop().dispose();
00283   }
00284 
00285   forceinline bool
00286   Path::steal(void) const {
00287     return n_work > Config::steal_limit;
00288   }
00289 
00290   forceinline Space*
00291   Path::steal(Worker& stat, unsigned long int& d) {
00292     // Find position to steal: leave sufficient work
00293     int n = ds.entries()-1;
00294     unsigned int w = 0;
00295     while (n >= 0) {
00296       if (ds[n].work())
00297         w++;
00298       if (w > Config::steal_limit) {
00299         // Okay, there is sufficient work left
00300         int l=n;
00301         // Find last copy
00302         while (ds[l].space() == NULL)
00303           l--;
00304         Space* c = ds[l].space()->clone(false);
00305         // Recompute, if necessary
00306         for (int i=l; i<n; i++)
00307           commit(c,i);
00308         c->commit(*ds[n].choice(),ds[n].steal());
00309         if (!ds[n].work())
00310           n_work--;
00311         d = stat.steal_depth(static_cast<unsigned long int>(n+1));
00312         return c;
00313       }
00314       n--;
00315     }
00316     return NULL;
00317   }
00318 
00319   forceinline Space*
00320   Path::recompute(unsigned int& d, unsigned int a_d, Worker& stat) {
00321     assert(!ds.empty());
00322     // Recompute space according to path
00323     // Also say distance to copy (d == 0) requires immediate copying
00324 
00325     // Check for LAO
00326     if ((ds.top().space() != NULL) && ds.top().rightmost()) {
00327       Space* s = ds.top().space();
00328       stat.lao(s);
00329       s->commit(*ds.top().choice(),ds.top().alt());
00330       assert(ds.entries()-1 == lc());
00331       ds.top().space(NULL);
00332       d = 0;
00333       return s;
00334     }
00335     // General case for recomputation
00336     int l = lc();             // Position of last clone
00337     int n = ds.entries();     // Number of stack entries
00338     // New distance, if no adaptive recomputation
00339     d = static_cast<unsigned int>(n - l);
00340 
00341     Space* s = ds[l].space()->clone(); // Last clone
00342 
00343     if (d < a_d) {
00344       // No adaptive recomputation
00345       for (int i=l; i<n; i++)
00346         commit(s,i);
00347     } else {
00348       int m = l + static_cast<int>(d >> 1); // Middle between copy and top
00349       int i = l; // To iterate over all entries
00350       // Recompute up to middle
00351       for (; i<m; i++ )
00352         commit(s,i);
00353       // Skip over all rightmost branches
00354       for (; (i<n) && ds[i].rightmost(); i++)
00355         commit(s,i);
00356       // Is there any point to make a copy?
00357       if (i<n-1) {
00358         // Propagate to fixpoint
00359         SpaceStatus ss = s->status(stat);
00360         /*
00361          * Again, the space might already propagate to failure (due to
00362          * weakly monotonic propagators).
00363          */
00364         if (ss == SS_FAILED) {
00365           // s must be deleted as it is not on the stack
00366           delete s;
00367           stat.fail++;
00368           unwind(i);
00369           return NULL;
00370         }
00371         ds[i].space(s->clone());
00372         stat.adapt(ds[i].space());
00373         d = static_cast<unsigned int>(n-i);
00374       }
00375       // Finally do the remaining commits
00376       for (; i<n; i++)
00377         commit(s,i);
00378     }
00379     return s;
00380   }
00381 
00382   forceinline Space*
00383   Path::recompute(unsigned int& d, unsigned int a_d, Worker& stat,
00384                   const Space* best, int& mark) {
00385     assert(!ds.empty());
00386     // Recompute space according to path
00387     // Also say distance to copy (d == 0) requires immediate copying
00388 
00389     // Check for LAO
00390     if ((ds.top().space() != NULL) && ds.top().rightmost()) {
00391       Space* s = ds.top().space();
00392       stat.lao(s);
00393       s->commit(*ds.top().choice(),ds.top().alt());
00394       assert(ds.entries()-1 == lc());
00395       if (mark > ds.entries()-1) {
00396         mark = ds.entries()-1;
00397         s->constrain(*best);
00398       }
00399       ds.top().space(NULL);
00400       d = 0;
00401       return s;
00402     }
00403     // General case for recomputation
00404     int l = lc();             // Position of last clone
00405     int n = ds.entries();     // Number of stack entries
00406     // New distance, if no adaptive recomputation
00407     d = static_cast<unsigned int>(n - l);
00408 
00409     Space* s = ds[l].space(); // Last clone
00410 
00411     if (l < mark) {
00412       mark = l;
00413       s->constrain(*best);
00414       // The space on the stack could be failed now as an additional
00415       // constraint might have been added.
00416       if (s->status(stat) == SS_FAILED) {
00417         // s does not need deletion as it is on the stack (unwind does this)
00418         stat.fail++;
00419         unwind(l);
00420         return NULL;
00421       }
00422       // It is important to replace the space on the stack with the
00423       // copy: a copy might be much smaller due to flushed caches
00424       // of propagators
00425       Space* c = s->clone();
00426       ds[l].space(c);
00427       stat.constrained(s,c);
00428     } else {
00429       s = s->clone();
00430     }
00431 
00432     if (d < a_d) {
00433       // No adaptive recomputation
00434       for (int i=l; i<n; i++)
00435         commit(s,i);
00436     } else {
00437       int m = l + static_cast<int>(d >> 1); // Middle between copy and top
00438       int i = l;            // To iterate over all entries
00439       // Recompute up to middle
00440       for (; i<m; i++ )
00441         commit(s,i);
00442       // Skip over all rightmost branches
00443       for (; (i<n) && ds[i].rightmost(); i++)
00444         commit(s,i);
00445       // Is there any point to make a copy?
00446       if (i<n-1) {
00447         // Propagate to fixpoint
00448         SpaceStatus ss = s->status(stat);
00449         /*
00450          * Again, the space might already propagate to failure
00451          *
00452          * This can be for two reasons:
00453          *  - constrain is true, so we fail
00454          *  - the space has weakly monotonic propagators
00455          */
00456         if (ss == SS_FAILED) {
00457           // s must be deleted as it is not on the stack
00458           delete s;
00459           stat.fail++;
00460           unwind(i);
00461           return NULL;
00462         }
00463         ds[i].space(s->clone());
00464         stat.adapt(ds[i].space());
00465         d = static_cast<unsigned int>(n-i);
00466       }
00467       // Finally do the remaining commits
00468       for (; i<n; i++)
00469         commit(s,i);
00470     }
00471     return s;
00472   }
00473 
00474 }}}
00475 
00476 #endif
00477 
00478 // STATISTICS: search-parallel