/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Christian Schulte * * Copyright: * Christian Schulte, 2004 * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include #include #include #include #ifndef GECODE_THREADS_WINDOWS #include #endif namespace Gecode { namespace Driver { /** * \brief Stop object based on nodes, failures, and time * */ class CombinedStop : public Search::Stop { private: Search::NodeStop* ns; ///< Used node stop object Search::FailStop* fs; ///< Used fail stop object Search::TimeStop* ts; ///< Used time stop object GECODE_DRIVER_EXPORT static bool sigint; ///< Whether search was interrupted using Ctrl-C /// Initialize stop object CombinedStop(unsigned long long int node, unsigned long long int fail, double time) : ns((node > 0ULL) ? new Search::NodeStop(node) : nullptr), fs((fail > 0ULL) ? new Search::FailStop(fail) : nullptr), ts((time > 0.0) ? new Search::TimeStop(time) : nullptr) { sigint = false; } public: /// Reason why search has been stopped enum { SR_NODE = 1 << 0, ///< Node limit reached SR_FAIL = 1 << 1, ///< Fail limit reached SR_TIME = 1 << 2, ///< Time limit reached SR_INT = 1 << 3 ///< Interrupted by user }; /// Test whether search must be stopped virtual bool stop(const Search::Statistics& s, const Search::Options& o) { return sigint || ((ns != nullptr) && ns->stop(s,o)) || ((fs != nullptr) && fs->stop(s,o)) || ((ts != nullptr) && ts->stop(s,o)); } /// Report reason why search has been stopped int reason(const Search::Statistics& s, const Search::Options& o) { return (((ns != nullptr) && ns->stop(s,o)) ? SR_NODE : 0) | (((fs != nullptr) && fs->stop(s,o)) ? SR_FAIL : 0) | (((ts != nullptr) && ts->stop(s,o)) ? SR_TIME : 0) | (sigint ? SR_INT : 0); } /// Create appropriate stop-object static Search::Stop* create(unsigned long long int node, unsigned long long int fail, double time, bool intr) { if (!intr && (node == 0ULL) && (fail == 0ULL) && (time == 0.0)) return nullptr; else return new CombinedStop(node,fail,time); } #ifdef GECODE_THREADS_WINDOWS /// Handler for catching Ctrl-C static BOOL interrupt(DWORD t) noexcept { if (t == CTRL_C_EVENT) { sigint = true; installCtrlHandler(false,true); return true; } return false; } #else /// Handler for catching Ctrl-C static void interrupt(int) { sigint = true; installCtrlHandler(false,true); } #endif /// Install handler for catching Ctrl-C static void installCtrlHandler(bool install, bool force=false) { if (force || !sigint) { #ifdef GECODE_THREADS_WINDOWS SetConsoleCtrlHandler( (PHANDLER_ROUTINE) interrupt, install); #else std::signal(SIGINT, install ? interrupt : SIG_DFL); #endif } } /// Destructor ~CombinedStop(void) { delete ns; delete fs; delete ts; } }; /** * \brief Get time since start of timer and print user friendly time * information. */ GECODE_DRIVER_EXPORT void stop(Support::Timer& t, std::ostream& os); /** * \brief Compute arithmetic mean of \a n elements in \a t */ GECODE_DRIVER_EXPORT double am(double t[], unsigned int n); /** * \brief Compute deviation of \a n elements in \a t */ GECODE_DRIVER_EXPORT double dev(double t[], unsigned int n); /// Create cutoff object from options template inline Search::Cutoff* createCutoff(const Options& o) { switch (o.restart()) { case RM_NONE: return nullptr; case RM_CONSTANT: return Search::Cutoff::constant(o.restart_scale()); case RM_LINEAR: return Search::Cutoff::linear(o.restart_scale()); case RM_LUBY: return Search::Cutoff::luby(o.restart_scale()); case RM_GEOMETRIC: return Search::Cutoff::geometric(o.restart_scale(),o.restart_base()); default: GECODE_NEVER; } return nullptr; } #ifdef GECODE_HAS_GIST /** * \brief Traits class for search engines */ template class GistEngine { public: static void explore(Space* root, const Gist::Options& opt) { (void) Gist::dfs(root, opt); } }; /// Specialization for DFS template class GistEngine > { public: static void explore(S* root, const Gist::Options& opt) { (void) Gist::dfs(root, opt); } }; /// Specialization for LDS template class GistEngine > { public: static void explore(S* root, const Gist::Options& opt) { (void) Gist::dfs(root, opt); } }; /// Specialization for BAB template class GistEngine > { public: static void explore(S* root, const Gist::Options& opt) { (void) Gist::bab(root, opt); } }; #endif #ifdef GECODE_HAS_CPPROFILER /// Class to send solution information to CPProfiler for a script template class ScriptGetInfo : public CPProfilerSearchTracer::GetInfo { public: /// Initialize ScriptGetInfo(void); /// Return info for a space (which must be a script) virtual std::string getInfo(const Space& home) const; }; #endif template forceinline ScriptBase::ScriptBase(const Options& opt) : BaseSpace(opt) {} template forceinline ScriptBase::ScriptBase(ScriptBase& e) : BaseSpace(e) {} template void ScriptBase::print(std::ostream&) const {} template void ScriptBase::compare(const Space&, std::ostream&) const {} template std::ostream& ScriptBase::select_ostream(const char* sn, std::ofstream& ofs) { if (strcmp(sn, "stdout") == 0) { return std::cout; } else if (strcmp(sn, "stdlog") == 0) { return std::clog; } else if (strcmp(sn, "stderr") == 0) { return std::cerr; } else { ofs.open(sn); return ofs; } } #ifdef GECODE_HAS_CPPROFILER template ScriptGetInfo::ScriptGetInfo(void) {} template std::string ScriptGetInfo::getInfo(const Space& home) const { std::stringstream ss; if (const ScriptBase* sb = dynamic_cast*>(&home)) sb->print(ss); return ss.str(); } #endif /** * \brief Wrapper class to add engine template argument */ template class E> class EngineToMeta : public E { public: EngineToMeta(T* s, const Search::Options& o) : E(s,o) {} }; template template class Engine, class Options> void ScriptBase::run(const Options& o, Script* s) { if ((o.restart() != RM_NONE) && (o.assets() > 0)) { std::cerr << "Cannot use restarts and portfolio..." << std::endl; exit(EXIT_FAILURE); } if (o.restart() != RM_NONE) { runMeta(o,s); } else if (o.assets() > 0) { runMeta(o,s); } else { runMeta(o,s); } } template template class Engine, class Options, template class> class Meta> void ScriptBase::runMeta(const Options& o, Script* s) { using namespace std; ofstream sol_file, log_file; ostream& s_out = select_ostream(o.out_file(), sol_file); ostream& l_out = select_ostream(o.log_file(), log_file); Search::Options so; try { switch (o.mode()) { case SM_GIST: #ifdef GECODE_HAS_GIST { Gist::Print