gecode » search » parallel

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