this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2/* 3 * Main authors: 4 * Christian Schulte <schulte@gecode.org> 5 * 6 * Copyright: 7 * Christian Schulte, 2004 8 * 9 * This file is part of Gecode, the generic constraint 10 * development environment: 11 * http://www.gecode.org 12 * 13 * 14 * Permission is hereby granted, free of charge, to any person obtaining 15 * a copy of this software and associated documentation files (the 16 * "Software"), to deal in the Software without restriction, including 17 * without limitation the rights to use, copy, modify, merge, publish, 18 * distribute, sublicense, and/or sell copies of the Software, and to 19 * permit persons to whom the Software is furnished to do so, subject to 20 * the following conditions: 21 * 22 * The above copyright notice and this permission notice shall be 23 * included in all copies or substantial portions of the Software. 24 * 25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 29 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 30 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 31 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 * 33 */ 34 35#include <iostream> 36#include <iomanip> 37#include <fstream> 38#include <cstring> 39 40#ifndef GECODE_THREADS_WINDOWS 41#include <csignal> 42#endif 43 44namespace Gecode { namespace Driver { 45 46 /** 47 * \brief Stop object based on nodes, failures, and time 48 * 49 */ 50 class CombinedStop : public Search::Stop { 51 private: 52 Search::NodeStop* ns; ///< Used node stop object 53 Search::FailStop* fs; ///< Used fail stop object 54 Search::TimeStop* ts; ///< Used time stop object 55 GECODE_DRIVER_EXPORT 56 static bool sigint; ///< Whether search was interrupted using Ctrl-C 57 /// Initialize stop object 58 CombinedStop(unsigned long long int node, 59 unsigned long long int fail, 60 double time) 61 : ns((node > 0ULL) ? new Search::NodeStop(node) : nullptr), 62 fs((fail > 0ULL) ? new Search::FailStop(fail) : nullptr), 63 ts((time > 0.0) ? new Search::TimeStop(time) : nullptr) { 64 sigint = false; 65 } 66 public: 67 /// Reason why search has been stopped 68 enum { 69 SR_NODE = 1 << 0, ///< Node limit reached 70 SR_FAIL = 1 << 1, ///< Fail limit reached 71 SR_TIME = 1 << 2, ///< Time limit reached 72 SR_INT = 1 << 3 ///< Interrupted by user 73 }; 74 /// Test whether search must be stopped 75 virtual bool stop(const Search::Statistics& s, const Search::Options& o) { 76 return 77 sigint || 78 ((ns != nullptr) && ns->stop(s,o)) || 79 ((fs != nullptr) && fs->stop(s,o)) || 80 ((ts != nullptr) && ts->stop(s,o)); 81 } 82 /// Report reason why search has been stopped 83 int reason(const Search::Statistics& s, const Search::Options& o) { 84 return 85 (((ns != nullptr) && ns->stop(s,o)) ? SR_NODE : 0) | 86 (((fs != nullptr) && fs->stop(s,o)) ? SR_FAIL : 0) | 87 (((ts != nullptr) && ts->stop(s,o)) ? SR_TIME : 0) | 88 (sigint ? SR_INT : 0); 89 } 90 /// Create appropriate stop-object 91 static Search::Stop* 92 create(unsigned long long int node, 93 unsigned long long int fail, 94 double time, 95 bool intr) { 96 if (!intr && (node == 0ULL) && (fail == 0ULL) && (time == 0.0)) 97 return nullptr; 98 else 99 return new CombinedStop(node,fail,time); 100 } 101#ifdef GECODE_THREADS_WINDOWS 102 /// Handler for catching Ctrl-C 103 static BOOL interrupt(DWORD t) noexcept { 104 if (t == CTRL_C_EVENT) { 105 sigint = true; 106 installCtrlHandler(false,true); 107 return true; 108 } 109 return false; 110 } 111#else 112 /// Handler for catching Ctrl-C 113 static void 114 interrupt(int) { 115 sigint = true; 116 installCtrlHandler(false,true); 117 } 118#endif 119 /// Install handler for catching Ctrl-C 120 static void installCtrlHandler(bool install, bool force=false) { 121 if (force || !sigint) { 122#ifdef GECODE_THREADS_WINDOWS 123 SetConsoleCtrlHandler( (PHANDLER_ROUTINE) interrupt, install); 124#else 125 std::signal(SIGINT, install ? interrupt : SIG_DFL); 126#endif 127 } 128 } 129 /// Destructor 130 ~CombinedStop(void) { 131 delete ns; delete fs; delete ts; 132 } 133 }; 134 135 /** 136 * \brief Get time since start of timer and print user friendly time 137 * information. 138 */ 139 GECODE_DRIVER_EXPORT void 140 stop(Support::Timer& t, std::ostream& os); 141 142 /** 143 * \brief Compute arithmetic mean of \a n elements in \a t 144 */ 145 GECODE_DRIVER_EXPORT double 146 am(double t[], unsigned int n); 147 148 /** 149 * \brief Compute deviation of \a n elements in \a t 150 */ 151 GECODE_DRIVER_EXPORT double 152 dev(double t[], unsigned int n); 153 154 /// Create cutoff object from options 155 template<class Options> 156 inline Search::Cutoff* 157 createCutoff(const Options& o) { 158 switch (o.restart()) { 159 case RM_NONE: 160 return nullptr; 161 case RM_CONSTANT: 162 return Search::Cutoff::constant(o.restart_scale()); 163 case RM_LINEAR: 164 return Search::Cutoff::linear(o.restart_scale()); 165 case RM_LUBY: 166 return Search::Cutoff::luby(o.restart_scale()); 167 case RM_GEOMETRIC: 168 return Search::Cutoff::geometric(o.restart_scale(),o.restart_base()); 169 default: GECODE_NEVER; 170 } 171 return nullptr; 172 } 173 174 175#ifdef GECODE_HAS_GIST 176 177 /** 178 * \brief Traits class for search engines 179 */ 180 template<class Engine> 181 class GistEngine { 182 public: 183 static void explore(Space* root, const Gist::Options& opt) { 184 (void) Gist::dfs(root, opt); 185 } 186 }; 187 188 /// Specialization for DFS 189 template<typename S> 190 class GistEngine<DFS<S> > { 191 public: 192 static void explore(S* root, const Gist::Options& opt) { 193 (void) Gist::dfs(root, opt); 194 } 195 }; 196 197 /// Specialization for LDS 198 template<typename S> 199 class GistEngine<LDS<S> > { 200 public: 201 static void explore(S* root, const Gist::Options& opt) { 202 (void) Gist::dfs(root, opt); 203 } 204 }; 205 206 /// Specialization for BAB 207 template<typename S> 208 class GistEngine<BAB<S> > { 209 public: 210 static void explore(S* root, const Gist::Options& opt) { 211 (void) Gist::bab(root, opt); 212 } 213 }; 214 215#endif 216 217#ifdef GECODE_HAS_CPPROFILER 218 219 /// Class to send solution information to CPProfiler for a script 220 template<class BaseSpace> 221 class ScriptGetInfo : public CPProfilerSearchTracer::GetInfo { 222 public: 223 /// Initialize 224 ScriptGetInfo(void); 225 /// Return info for a space (which must be a script) 226 virtual std::string getInfo(const Space& home) const; 227 }; 228 229#endif 230 231 template<class BaseSpace> 232 forceinline 233 ScriptBase<BaseSpace>::ScriptBase(const Options& opt) 234 : BaseSpace(opt) {} 235 236 template<class BaseSpace> 237 forceinline 238 ScriptBase<BaseSpace>::ScriptBase(ScriptBase& e) 239 : BaseSpace(e) {} 240 241 template<class BaseSpace> 242 void 243 ScriptBase<BaseSpace>::print(std::ostream&) const {} 244 245 template<class BaseSpace> 246 void 247 ScriptBase<BaseSpace>::compare(const Space&, std::ostream&) const {} 248 249 template<class BaseSpace> 250 std::ostream& 251 ScriptBase<BaseSpace>::select_ostream(const char* sn, std::ofstream& ofs) { 252 if (strcmp(sn, "stdout") == 0) { 253 return std::cout; 254 } else if (strcmp(sn, "stdlog") == 0) { 255 return std::clog; 256 } else if (strcmp(sn, "stderr") == 0) { 257 return std::cerr; 258 } else { 259 ofs.open(sn); 260 return ofs; 261 } 262 } 263 264#ifdef GECODE_HAS_CPPROFILER 265 266 template<class BaseSpace> 267 ScriptGetInfo<BaseSpace>::ScriptGetInfo(void) {} 268 269 template<class BaseSpace> 270 std::string 271 ScriptGetInfo<BaseSpace>::getInfo(const Space& home) const { 272 std::stringstream ss; 273 if (const ScriptBase<BaseSpace>* sb 274 = dynamic_cast<const ScriptBase<BaseSpace>*>(&home)) 275 sb->print(ss); 276 return ss.str(); 277 } 278 279#endif 280 281 282 /** 283 * \brief Wrapper class to add engine template argument 284 */ 285 template<class T, template<class> class E> 286 class EngineToMeta : public E<T> { 287 public: 288 EngineToMeta(T* s, const Search::Options& o) : E<T>(s,o) {} 289 }; 290 291 template<class BaseSpace> 292 template<class Script, template<class> class Engine, class Options> 293 void 294 ScriptBase<BaseSpace>::run(const Options& o, Script* s) { 295 if ((o.restart() != RM_NONE) && (o.assets() > 0)) { 296 std::cerr << "Cannot use restarts and portfolio..." << std::endl; 297 exit(EXIT_FAILURE); 298 } 299 if (o.restart() != RM_NONE) { 300 runMeta<Script,Engine,Options,RBS>(o,s); 301 } else if (o.assets() > 0) { 302 runMeta<Script,Engine,Options,PBS>(o,s); 303 } else { 304 runMeta<Script,Engine,Options,EngineToMeta>(o,s); 305 } 306 } 307 308 template<class BaseSpace> 309 template<class Script, template<class> class Engine, class Options, 310 template<class, template<class> class> class Meta> 311 void 312 ScriptBase<BaseSpace>::runMeta(const Options& o, Script* s) { 313 using namespace std; 314 315 ofstream sol_file, log_file; 316 317 ostream& s_out = select_ostream(o.out_file(), sol_file); 318 ostream& l_out = select_ostream(o.log_file(), log_file); 319 320 Search::Options so; 321 322 try { 323 switch (o.mode()) { 324 case SM_GIST: 325#ifdef GECODE_HAS_GIST 326 { 327 Gist::Print<Script> pi(o.name()); 328 Gist::VarComparator<Script> vc(o.name()); 329 Gist::Options opt; 330 opt.inspect.click(&pi); 331 opt.inspect.compare(&vc); 332 opt.clone = false; 333 opt.c_d = o.c_d(); 334 opt.a_d = o.a_d(); 335 for (unsigned int i=0; o.inspect.click(i) != nullptr; i++) 336 opt.inspect.click(o.inspect.click(i)); 337 for (unsigned int i=0; o.inspect.solution(i) != nullptr; i++) 338 opt.inspect.solution(o.inspect.solution(i)); 339 for (unsigned int i=0; o.inspect.move(i) != nullptr; i++) 340 opt.inspect.move(o.inspect.move(i)); 341 for (unsigned int i=0; o.inspect.compare(i) != nullptr; i++) 342 opt.inspect.compare(o.inspect.compare(i)); 343 if (s == nullptr) 344 s = new Script(o); 345 (void) GistEngine<Engine<Script> >::explore(s, opt); 346 } 347 break; 348 // If Gist is not available, goto solution 349#else 350 goto solution; 351#endif 352 case SM_SOLUTION: 353#ifndef GECODE_HAS_GIST 354 solution: 355#endif 356 { 357#ifdef GECODE_HAS_CPPROFILER 358 if (o.profiler_port()) { 359 CPProfilerSearchTracer::GetInfo* getInfo = nullptr; 360 if (o.profiler_info()) 361 getInfo = new ScriptGetInfo<BaseSpace>; 362 so.tracer = new CPProfilerSearchTracer 363 (o.profiler_id(), o.name(), o.profiler_port(), getInfo); 364 } 365#endif 366 l_out << o.name() << endl; 367 Support::Timer t; 368 unsigned long long int s_l = 369 (o.solutions() == 0) ? ULLONG_MAX : o.solutions(); 370 unsigned long long int s_n = 0; 371 t.start(); 372 if (s == nullptr) 373 s = new Script(o); 374 unsigned int n_p = PropagatorGroup::all.size(*s); 375 unsigned int n_b = BrancherGroup::all.size(*s); 376 so.threads = o.threads(); 377 so.c_d = o.c_d(); 378 so.a_d = o.a_d(); 379 so.d_l = o.d_l(); 380 so.assets = o.assets(); 381 so.slice = o.slice(); 382 so.stop = CombinedStop::create(o.node(),o.fail(), o.time(), 383 o.interrupt()); 384 so.cutoff = createCutoff(o); 385 so.clone = false; 386 so.nogoods_limit = o.nogoods() ? o.nogoods_limit() : 0U; 387 if (o.interrupt()) 388 CombinedStop::installCtrlHandler(true); 389 { 390 Meta<Script,Engine> e(s,so); 391 if (o.print_last()) { 392 Script* px = nullptr; 393 do { 394 Script* ex = e.next(); 395 if (ex == nullptr) { 396 if (px != nullptr) { 397 px->print(s_out); 398 delete px; 399 } 400 break; 401 } else { 402 s_n++; 403 delete px; 404 px = ex; 405 } 406 } while (s_n < s_l); 407 } else { 408 do { 409 Script* ex = e.next(); 410 if (ex == nullptr) 411 break; 412 ex->print(s_out); 413 delete ex; 414 s_n++; 415 } while (s_n < s_l); 416 } 417 if (o.interrupt()) 418 CombinedStop::installCtrlHandler(false); 419 Search::Statistics stat = e.statistics(); 420 s_out << endl; 421 if (e.stopped()) { 422 l_out << "Search engine stopped..." << endl 423 << "\treason: "; 424 int r = static_cast<CombinedStop*>(so.stop)->reason(stat,so); 425 if (r & CombinedStop::SR_INT) 426 l_out << "user interrupt " << endl; 427 else { 428 if (r & CombinedStop::SR_NODE) 429 l_out << "node "; 430 if (r & CombinedStop::SR_FAIL) 431 l_out << "fail "; 432 if (r & CombinedStop::SR_TIME) 433 l_out << "time "; 434 l_out << "limit reached" << endl << endl; 435 } 436 } 437 l_out << "Initial" << endl 438 << "\tpropagators: " << n_p << endl 439 << "\tbranchers: " << n_b << endl 440 << endl 441 << "Summary" << endl 442 << "\truntime: "; 443 stop(t, l_out); 444 l_out << endl 445 << "\tsolutions: " << s_n << endl 446 << "\tpropagations: " << stat.propagate << endl 447 << "\tnodes: " << stat.node << endl 448 << "\tfailures: " << stat.fail << endl 449 << "\trestarts: " << stat.restart << endl 450 << "\tno-goods: " << stat.nogood << endl 451 << "\tpeak depth: " << stat.depth << endl 452#ifdef GECODE_PEAKHEAP 453 << "\tpeak memory: " 454 << static_cast<int>((heap.peak()+1023) / 1024) << " KB" 455 << endl 456#endif 457 << endl; 458 } 459 delete so.stop; 460 delete so.tracer; 461 } 462 break; 463 case SM_STAT: 464 { 465 l_out << o.name() << endl; 466 Support::Timer t; 467 unsigned long long int s_l = 468 (o.solutions() == 0) ? ULLONG_MAX : o.solutions(); 469 unsigned long long int s_n = 0; 470 t.start(); 471 if (s == nullptr) 472 s = new Script(o); 473 unsigned int n_p = PropagatorGroup::all.size(*s); 474 unsigned int n_b = BrancherGroup::all.size(*s); 475 476 so.clone = false; 477 so.threads = o.threads(); 478 so.assets = o.assets(); 479 so.slice = o.slice(); 480 so.c_d = o.c_d(); 481 so.a_d = o.a_d(); 482 so.d_l = o.d_l(); 483 so.stop = CombinedStop::create(o.node(),o.fail(), o.time(), 484 o.interrupt()); 485 so.cutoff = createCutoff(o); 486 so.nogoods_limit = o.nogoods() ? o.nogoods_limit() : 0U; 487 if (o.interrupt()) 488 CombinedStop::installCtrlHandler(true); 489 { 490 Meta<Script,Engine> e(s,so); 491 do { 492 Script* ex = e.next(); 493 if (ex == nullptr) 494 break; 495 delete ex; 496 s_n++; 497 } while (s_n < s_l); 498 if (o.interrupt()) 499 CombinedStop::installCtrlHandler(false); 500 Search::Statistics stat = e.statistics(); 501 l_out << endl 502 << "\tpropagators: " << n_p << endl 503 << "\tbranchers: " << n_b << endl 504 << "\truntime: "; 505 stop(t, l_out); 506 l_out << endl 507 << "\tsolutions: " << s_n << endl 508 << "\tpropagations: " << stat.propagate << endl 509 << "\tnodes: " << stat.node << endl 510 << "\tfailures: " << stat.fail << endl 511 << "\trestarts: " << stat.restart << endl 512 << "\tno-goods: " << stat.nogood << endl 513 << "\tpeak depth: " << stat.depth << endl 514#ifdef GECODE_PEAKHEAP 515 << "\tpeak memory: " 516 << static_cast<int>((heap.peak()+1023) / 1024) << " KB" 517 << endl 518#endif 519 << endl; 520 } 521 delete so.stop; 522 } 523 break; 524 case SM_TIME: 525 { 526 l_out << o.name() << endl; 527 Support::Timer t; 528 double* ts = new double[o.samples()]; 529 bool stopped = false; 530 for (unsigned int ns = o.samples(); !stopped && ns--; ) { 531 unsigned long long int s_l = 532 (o.solutions() == 0) ? ULLONG_MAX : o.solutions(); 533 t.start(); 534 for (unsigned int k = o.iterations(); !stopped && k--; ) { 535 unsigned long long int s_n = 0; 536 Script* s1 = new Script(o); 537 Search::Options sok; 538 sok.clone = false; 539 sok.threads = o.threads(); 540 sok.assets = o.assets(); 541 sok.slice = o.slice(); 542 sok.c_d = o.c_d(); 543 sok.a_d = o.a_d(); 544 sok.d_l = o.d_l(); 545 sok.stop = CombinedStop::create(o.node(),o.fail(), o.time(), 546 false); 547 sok.cutoff = createCutoff(o); 548 sok.nogoods_limit = o.nogoods() ? o.nogoods_limit() : 0U; 549 { 550 Meta<Script,Engine> e(s1,sok); 551 do { 552 Script* ex = e.next(); 553 if (ex == nullptr) 554 break; 555 delete ex; 556 s_n++; 557 } while (s_n < s_l); 558 if (e.stopped()) 559 stopped = true; 560 } 561 delete sok.stop; 562 } 563 ts[ns] = t.stop() / o.iterations(); 564 } 565 if (stopped) { 566 l_out << "\tSTOPPED" << endl; 567 } else { 568 double m = am(ts,o.samples()); 569 double d = dev(ts,o.samples()) * 100.0; 570 l_out << "\truntime: " 571 << setw(20) << right 572 << showpoint << fixed 573 << setprecision(6) << m << "ms" 574 << setprecision(2) << " (" << d << "% deviation)" 575 << endl; 576 } 577 delete [] ts; 578 } 579 break; 580 } 581 } catch (Exception& e) { 582 cerr << "Exception: " << e.what() << "." << endl 583 << "Stopping..." << endl; 584 if (sol_file.is_open()) 585 sol_file.close(); 586 if (log_file.is_open()) 587 log_file.close(); 588 exit(EXIT_FAILURE); 589 } 590 if (sol_file.is_open()) 591 sol_file.close(); 592 if (log_file.is_open()) 593 log_file.close(); 594 } 595 596}} 597 598// STATISTICS: driver-any