dlvhex
2.5.0
|
00001 /* dlvhex -- Answer-Set Programming with external interfaces. 00002 * Copyright (C) 2005-2007 Roman Schindlauer 00003 * Copyright (C) 2006-2015 Thomas Krennwallner 00004 * Copyright (C) 2009-2016 Peter Schüller 00005 * Copyright (C) 2011-2016 Christoph Redl 00006 * Copyright (C) 2015-2016 Tobias Kaminski 00007 * Copyright (C) 2015-2016 Antonius Weinzierl 00008 * 00009 * This file is part of dlvhex. 00010 * 00011 * dlvhex is free software; you can redistribute it and/or modify it 00012 * under the terms of the GNU Lesser General Public License as 00013 * published by the Free Software Foundation; either version 2.1 of 00014 * the License, or (at your option) any later version. 00015 * 00016 * dlvhex is distributed in the hope that it will be useful, but 00017 * WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with dlvhex; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00024 * 02110-1301 USA. 00025 */ 00026 00035 #ifdef HAVE_CONFIG_H 00036 # include "config.h" 00037 #endif 00038 00039 #ifdef HAVE_LIBCLINGO 00040 00041 #include "dlvhex2/ASPSolver.h" 00042 #include "dlvhex2/PlatformDefinitions.h" 00043 #include "dlvhex2/Benchmarking.h" 00044 #include "dlvhex2/Printer.h" 00045 #include "dlvhex2/Registry.h" 00046 #include "dlvhex2/ProgramCtx.h" 00047 #include "dlvhex2/AnswerSet.h" 00048 00049 #include <clingo/clingo_app.h> 00050 00051 #include <boost/tokenizer.hpp> 00052 #include <boost/thread.hpp> 00053 #include <boost/shared_ptr.hpp> 00054 #include <boost/foreach.hpp> 00055 #include <list> 00056 00057 DLVHEX_NAMESPACE_BEGIN 00058 00059 namespace ASPSolver 00060 { 00061 00062 // 00063 // ClingoSoftware 00064 // 00065 ClingoSoftware::Options::Options(): 00066 ASPSolverManager::GenericOptions() { 00067 } 00068 00069 ClingoSoftware::Options::~Options() { 00070 } 00071 00072 namespace 00073 { 00074 00075 class GringoPrinter: 00076 public RawPrinter 00077 { 00078 public: 00079 typedef RawPrinter Base; 00080 GringoPrinter(std::ostream& out, RegistryPtr registry): 00081 RawPrinter(out, registry) {} 00082 00083 virtual void print(ID id) { 00084 if( id.isRule() && id.isRegularRule() ) { 00085 // disjunction in rule heads is | not v 00086 const Rule& r = registry->rules.getByID(id); 00087 printmany(r.head, " | "); 00088 if( !r.body.empty() ) { 00089 out << " :- "; 00090 printmany(r.body, ", "); 00091 } 00092 out << "."; 00093 } 00094 else { 00095 Base::print(id); 00096 } 00097 } 00098 }; 00099 00100 class ClingoResults: 00101 public ASPSolverManager::Results 00102 { 00103 public: 00104 typedef std::list<AnswerSet::Ptr> Storage; 00105 Storage answersets; 00106 bool resetCurrent; 00107 Storage::const_iterator current; 00108 00109 ClingoResults(): 00110 resetCurrent(true), 00111 current() {} 00112 virtual ~ClingoResults() {} 00113 00114 void add(AnswerSet::Ptr as) { 00115 answersets.push_back(as); 00116 00117 // we do this because I'm not sure if a begin()==end() iterator 00118 // becomes begin() or end() after insertion of the first element 00119 // (this is the failsafe version) 00120 if( resetCurrent ) { 00121 current = answersets.begin(); 00122 resetCurrent = false; 00123 } 00124 } 00125 00126 virtual AnswerSet::Ptr getNextAnswerSet() { 00127 // if no answer set was ever added, or we reached the end 00128 if( (resetCurrent == true) || 00129 (current == answersets.end()) ) { 00130 return AnswerSet::Ptr(); 00131 } 00132 else { 00133 Storage::const_iterator ret = current; 00134 current++; 00135 return *ret; 00136 } 00137 } 00138 }; 00139 00140 class ClaspTermination: 00141 public std::runtime_error 00142 { 00143 public: 00144 ClaspTermination(): 00145 std::runtime_error("ClaspTermination") { 00146 } 00147 }; 00148 00149 class MyClaspOutputFormat: 00150 public Clasp::OutputFormat 00151 { 00152 public: 00153 typedef Clasp::OutputFormat Base; 00154 ConcurrentQueueResults& results; 00155 bool& shouldTerminate; 00156 RegistryPtr registry; 00157 InterpretationConstPtr mask; 00158 00159 MyClaspOutputFormat( 00160 ConcurrentQueueResults& res, 00161 bool& shouldTerminate, 00162 RegistryPtr registry, 00163 InterpretationConstPtr mask): 00164 Base(), 00165 results(res), 00166 shouldTerminate(shouldTerminate), 00167 registry(registry), 00168 mask(mask) { 00169 } 00170 00171 virtual ~MyClaspOutputFormat() { 00172 } 00173 00174 virtual void printModel( 00175 const Clasp::Solver& s, const Clasp::Enumerator&) { 00176 ConcurrentQueueResults* results = &this->results; 00177 DBGLOG(DBG,"getting model from clingo!"); 00178 00179 if( shouldTerminate ) { 00180 DBGLOG(DBG,"terminating (1) not enqueueing anything"); 00181 throw ClaspTermination(); 00182 } 00183 00184 AnswerSet::Ptr as(new AnswerSet(registry)); 00185 00186 const Clasp::AtomIndex& index = *s.strategies().symTab; 00187 for (Clasp::AtomIndex::const_iterator it = index.begin(); it != index.end(); ++it) { 00188 if (s.value(it->second.lit.var()) == Clasp::trueValue(it->second.lit) && !it->second.name.empty()) { 00189 const char* groundatom = it->second.name.c_str(); 00190 00191 // try to do it via string (unstructured) 00192 ID idga = registry->ogatoms.getIDByString(groundatom); 00193 if( idga == ID_FAIL ) { 00194 // parse groundatom, register and store 00195 DBGLOG(DBG,"parsing clingo ground atom '" << groundatom << "'"); 00196 OrdinaryAtom ogatom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 00197 ogatom.text = groundatom; 00198 { 00199 // create ogatom.tuple 00200 boost::char_separator<char> sep(",()"); 00201 typedef boost::tokenizer<boost::char_separator<char> > tokenizer; 00202 tokenizer tok(ogatom.text, sep); 00203 for(tokenizer::iterator it = tok.begin(); 00204 it != tok.end(); ++it) { 00205 DBGLOG(DBG,"got token '" << *it << "'"); 00206 Term term(ID::MAINKIND_TERM, *it); 00207 // the following takes care of int vs const/string 00208 ID id = registry->storeTerm(term); 00209 assert(id != ID_FAIL); 00210 assert(!id.isVariableTerm()); 00211 if( id.isAuxiliary() ) 00212 ogatom.kind |= ID::PROPERTY_AUX; 00213 ogatom.tuple.push_back(id); 00214 } 00215 } 00216 idga = registry->ogatoms.storeAndGetID(ogatom); 00217 } 00218 assert(idga != ID_FAIL); 00219 as->interpretation->setFact(idga.address); 00220 } 00221 } 00222 00223 LOG(INFO,"got model from clingo: " << *as); 00224 //std::cout << this << "CLINGO MODEL" << std::endl << *as << std::endl; 00225 if( mask ) 00226 as->interpretation->getStorage() -= mask->getStorage(); 00227 results->enqueueAnswerset(as); 00228 00229 if( shouldTerminate ) { 00230 DBGLOG(DBG,"terminating (2) not enqueueing anything"); 00231 throw ClaspTermination(); 00232 } 00233 } 00234 00235 virtual void printStats( 00236 const Clasp::SolverStatistics&, const Clasp::Enumerator&) { 00237 } 00238 }; 00239 00240 class MyClingoApp: 00241 public ClingoApp<CLINGO> 00242 { 00243 public: 00244 typedef ClingoApp<CLINGO> Base; 00245 ConcurrentQueueResults& results; 00246 bool& shouldTerminate; 00247 00248 MyClingoApp(ConcurrentQueueResults& res, bool& shouldTerminate): 00249 Base(), 00250 results(res), 00251 shouldTerminate(shouldTerminate) { 00252 DBGLOG(DBG,"MyClingoApp()"); 00253 } 00254 00255 ~MyClingoApp() { 00256 DBGLOG(DBG,"~MyClingoApp()"); 00257 } 00258 00259 void solve(std::string& program, RegistryPtr registry, InterpretationConstPtr mask) { 00260 ConcurrentQueueResults* results = &this->results; 00261 try 00262 { 00263 00264 // configure like a binary 00265 { 00266 char execname[] = "clingo_within_dlvhex"; 00267 char shiftopt[] = "--shift"; 00268 char allmodelsopt[] = "-n 0"; 00269 char noverboseopt[] = "--verbose=0"; 00270 char* argv[] = { 00271 execname, 00272 shiftopt, 00273 allmodelsopt, 00274 noverboseopt, 00275 NULL 00276 }; 00277 int argc = 0; 00278 while(argv[argc] != NULL) 00279 argc++; 00280 DBGLOG(DBG,"passing " << argc << " arguments to gringo:" << 00281 printrange(std::vector<const char*>(&argv[0], &argv[argc]))); 00282 if(!parse(argc, argv)) 00283 throw std::runtime_error( messages.error.c_str() ); 00284 #ifndef NDEBUG 00285 printWarnings(); 00286 #endif 00287 } 00288 00289 // configure in out 00290 Streams s; 00291 LOG(DBG,"sending to clingo:" << std::endl << "===" << std::endl << program << std::endl << "==="); 00292 //std::cout << this << "CLINGO PROGRAM" << std::endl << program << std::endl; 00293 s.appendStream( 00294 Streams::StreamPtr(new std::istringstream(program)), 00295 "dlvhex_to_clingo"); 00296 in_.reset(new FromGringo<CLINGO>(*this, s)); 00297 out_.reset(new MyClaspOutputFormat(*results, shouldTerminate, registry, mask)); 00298 00299 Clasp::ClaspFacade clasp; 00300 facade_ = &clasp; 00301 clingo.iStats = false; 00302 clasp.solve(*in_, config_, this); 00303 DBGLOG(DBG,"clasp.solve finished normally"); 00304 } 00305 catch(const ClaspTermination& e) { 00306 WARNING("we should find another way (than throwing an exception) to abort model enumeration in clasp") 00307 DBGLOG(DBG,"got ClaspTermination exception"); 00308 } 00309 catch(const std::exception& e) { 00310 std::cerr << "got clingo exception " << e.what() << std::endl; 00311 throw; 00312 } 00313 catch(...) { 00314 std::cerr << "got very strange clingo exception!" << std::endl; 00315 throw; 00316 } 00317 } 00318 }; 00319 00320 // 00321 // ConcurrentQueueResultsImpl 00322 // 00323 // Delegate::Impl is used to prepare the result 00324 // this object would be destroyed long before the result will be destroyed 00325 // therefore its ownership is passed to the results 00326 // 00327 struct ConcurrentQueueResultsImpl: 00328 public ConcurrentQueueResults 00329 { 00330 public: 00331 MyClingoApp myclingo; 00332 ClingoSoftware::Options options; 00333 OrdinaryASPProgram program; 00334 bool shouldTerminate; 00335 boost::thread answerSetProcessingThread; 00336 00337 public: 00338 ConcurrentQueueResultsImpl( 00339 const ClingoSoftware::Options& options, 00340 const OrdinaryASPProgram& program): 00341 myclingo(*this, shouldTerminate), 00342 ConcurrentQueueResults(), 00343 options(options), 00344 program(program), 00345 shouldTerminate(false) { 00346 DBGLOG(DBG,"libclingo ConcurrentQueueResultsImpl()" << this); 00347 } 00348 00349 virtual ~ConcurrentQueueResultsImpl() { 00350 DBGLOG(DBG,"libclingo ~ConcurrentQueueResultsImpl()" << this); 00351 DBGLOG(DBG,"setting termination bool, emptying queue, and joining thread"); 00352 shouldTerminate = true; 00353 queue->flush(); 00354 DBGLOG(DBG,"joining thread"); 00355 answerSetProcessingThread.join(); 00356 DBGLOG(DBG,"done"); 00357 } 00358 00359 void answerSetProcessingThreadFunc(); 00360 00361 void startThread() { 00362 DBGLOG(DBG,"starting answer set processing thread"); 00363 answerSetProcessingThread = boost::thread( 00364 boost::bind( 00365 &ConcurrentQueueResultsImpl::answerSetProcessingThreadFunc, 00366 boost::ref(*this))); 00367 DBGLOG(DBG,"started answer set processing thread"); 00368 } 00369 }; 00370 typedef boost::shared_ptr<ConcurrentQueueResultsImpl> 00371 ConcurrentQueueResultsImplPtr; 00372 00373 void ConcurrentQueueResultsImpl::answerSetProcessingThreadFunc() { 00374 WARNING("create multithreaded logger by using thread-local storage for logger indent") 00375 DBGLOG(DBG,"[" << this << "]" " starting libclingo answerSetProcessingThreadFunc"); 00376 try 00377 { 00378 // output program to stream 00379 WARNING("TODO handle program.maxint for clingo") 00380 std::string str; 00381 { 00382 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidprepare,"prepare clingo input"); 00383 00384 std::ostringstream programStream; 00385 GringoPrinter printer(programStream, program.registry); 00386 00387 if( program.edb != 0 ) { 00388 // print edb interpretation as facts 00389 program.edb->printAsFacts(programStream); 00390 programStream << "\n"; 00391 } 00392 00393 printer.printmany(program.idb, "\n"); 00394 programStream << std::endl; 00395 str = programStream.str(); 00396 } 00397 00398 // start solver (this creates results in callback) 00399 myclingo.solve(str, program.registry, program.mask); 00400 DBGLOG(DBG,"[" << this << "]" "myclingo.solve terminated regularly"); 00401 00402 if( !shouldTerminate ) { 00403 // enqueue regular end 00404 enqueueEnd(); 00405 } 00406 } 00407 catch(const GeneralError& e) { 00408 std::stringstream s; 00409 s << "libclingo: got GeneralError exception " << e.getErrorMsg(); 00410 LOG(ERROR, "[" << this << "]" + s.str()); 00411 enqueueException(s.str()); 00412 } 00413 catch(const std::exception& e) { 00414 std::stringstream s; 00415 s << "libclingo: got std::exception " << e.what(); 00416 LOG(ERROR, "[" << this << "]" + s.str()); 00417 enqueueException(s.str()); 00418 } 00419 catch(...) { 00420 std::stringstream s; 00421 s << "libclingo: got other exception"; 00422 LOG(ERROR, "[" << this << "]" + s.str()); 00423 enqueueException(s.str()); 00424 } 00425 DBGLOG(DBG,"[" << this << "]" "exiting answerSetProcessingThreadFunc"); 00426 } 00427 00428 } 00429 00430 struct ClingoSoftware::Delegate::Impl 00431 { 00432 ConcurrentQueueResultsImplPtr results; 00433 const Options& options; 00434 Impl(const Options& options): 00435 results(), options(options) {} 00436 ~Impl() { } 00437 }; 00438 00439 ClingoSoftware::Delegate::Delegate(const Options& options): 00440 pimpl(new Impl(options)) { 00441 } 00442 00443 ClingoSoftware::Delegate::~Delegate() { 00444 } 00445 00446 /* 00447 void 00448 ClingoSoftware::Delegate::useInputProviderInput(InputProvider& inp, RegistryPtr reg) 00449 { 00450 throw std::runtime_error("TODO implement ClingoSoftware::Delegate::useInputProviderInput(const InputProvider& inp, RegistryPtr reg)"); 00451 } 00452 */ 00453 00454 void 00455 ClingoSoftware::Delegate::useASTInput(const OrdinaryASPProgram& program) { 00456 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"ClingoSoftware useASTInput"); 00457 00458 pimpl->results.reset( 00459 new ConcurrentQueueResultsImpl(pimpl->options, program)); 00460 pimpl->results->startThread(); 00461 } 00462 00463 ASPSolverManager::ResultsPtr 00464 ClingoSoftware::Delegate::getResults() { 00465 return pimpl->results; 00466 } 00467 00468 } // namespace ASPSolver 00469 00470 00471 DLVHEX_NAMESPACE_END 00472 #endif // HAVE_LIBCLINGO 00473 00474 00475 // vim:expandtab:ts=4:sw=4: 00476 // mode: C++ 00477 // End: