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 00034 #ifdef HAVE_CONFIG_H 00035 #include "config.h" 00036 #endif // HAVE_CONFIG_H 00037 00038 //#define BOOST_SPIRIT_DEBUG 00039 00040 #include "dlvhex2/QueryPlugin.h" 00041 #include "dlvhex2/PlatformDefinitions.h" 00042 #include "dlvhex2/ProgramCtx.h" 00043 #include "dlvhex2/Registry.h" 00044 #include "dlvhex2/Printer.h" 00045 #include "dlvhex2/Printhelpers.h" 00046 #include "dlvhex2/Logger.h" 00047 #include "dlvhex2/HexParser.h" 00048 #include "dlvhex2/HexParserModule.h" 00049 #include "dlvhex2/HexGrammar.h" 00050 00051 #include <boost/algorithm/string/predicate.hpp> 00052 #include <boost/lexical_cast.hpp> 00053 00054 DLVHEX_NAMESPACE_BEGIN 00055 00056 namespace spirit = boost::spirit; 00057 namespace qi = boost::spirit::qi; 00058 00059 QueryPlugin::CtxData::CtxData(): 00060 enabled(false), 00061 mode(DEFAULT), 00062 ground(false), 00063 query(), 00064 varAuxPred(ID_FAIL), 00065 novarAuxPred(ID_FAIL), 00066 allWitnesses(false) 00067 { 00068 } 00069 00070 00071 QueryPlugin::QueryPlugin(): 00072 PluginInterface() 00073 { 00074 setNameVersion("dlvhex-queryplugin[internal]", 2, 0, 0); 00075 } 00076 00077 00078 QueryPlugin::~QueryPlugin() 00079 { 00080 } 00081 00082 00083 // output help message for this plugin 00084 void QueryPlugin::printUsage(std::ostream& o) const 00085 { 00086 // 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789- 00087 o << " --query-enable[=true,false]" << std::endl 00088 << " Enable or disable the querying plugin (default is disabled)." << std::endl 00089 << " --query-brave Do brave reasoning." << std::endl 00090 << " --query-all Give all witnesses when doing ground reasoning." << std::endl 00091 << " --query-cautious Do cautious reasoning." << std::endl; 00092 } 00093 00094 00095 // accepted options: --query-enables --query-brave --query-cautious 00096 // 00097 // processes options for this plugin, and removes recognized options from pluginOptions 00098 // (do not free the pointers, the const char* directly come from argv) 00099 void QueryPlugin::processOptions( 00100 std::list<const char*>& pluginOptions, 00101 ProgramCtx& ctx) 00102 { 00103 QueryPlugin::CtxData& ctxdata = ctx.getPluginData<QueryPlugin>(); 00104 ctxdata.enabled = false; 00105 00106 typedef std::list<const char*>::iterator Iterator; 00107 Iterator it; 00108 WARNING("create (or reuse, maybe from potassco?) cmdline option processing facility") 00109 it = pluginOptions.begin(); 00110 while( it != pluginOptions.end() ) { 00111 bool processed = false; 00112 const std::string str(*it); 00113 if( boost::starts_with(str, "--query-enable" ) ) { 00114 std::string m = str.substr(std::string("--query-enable").length()); 00115 if (m == "" || m == "=true") { 00116 ctxdata.enabled = true; 00117 } 00118 else if (m == "=false") { 00119 ctxdata.enabled = false; 00120 } 00121 else { 00122 std::stringstream ss; 00123 ss << "Unknown --strongnegation-enable option: " << m; 00124 throw PluginError(ss.str()); 00125 } 00126 processed = true; 00127 } 00128 else if( str == "--query-brave" ) { 00129 ctxdata.mode = CtxData::BRAVE; 00130 processed = true; 00131 } 00132 else if( str == "--query-cautious" ) { 00133 ctxdata.mode = CtxData::CAUTIOUS; 00134 processed = true; 00135 } 00136 else if( str == "--query-all" ) { 00137 ctxdata.allWitnesses = true; 00138 processed = true; 00139 } 00140 00141 if( processed ) { 00142 // return value of erase: element after it, maybe end() 00143 DBGLOG(DBG,"QueryPlugin successfully processed option " << str); 00144 it = pluginOptions.erase(it); 00145 } 00146 else { 00147 it++; 00148 } 00149 } 00150 00151 // some checks 00152 if( ctxdata.mode != CtxData::DEFAULT ) { 00153 if( !ctxdata.enabled ) { 00154 LOG(WARNING,"querying mode selected, but plugin not enabled " 00155 "(automatically enabling)"); 00156 ctxdata.enabled = true; 00157 } 00158 } 00159 if( ctxdata.enabled && ctxdata.mode == CtxData::DEFAULT ) 00160 throw FatalError("querying plugin enabled but no querying mode selected"); 00161 } 00162 00163 00164 class QueryParserModuleSemantics: 00165 public HexGrammarSemantics 00166 { 00167 public: 00168 QueryPlugin::CtxData& ctxdata; 00169 00170 public: 00171 QueryParserModuleSemantics(ProgramCtx& ctx): 00172 HexGrammarSemantics(ctx), 00173 ctxdata(ctx.getPluginData<QueryPlugin>()) { 00174 } 00175 00176 // use SemanticActionBase to redirect semantic action call into globally 00177 // specializable sem<T> struct space 00178 struct queryBody: 00179 SemanticActionBase<QueryParserModuleSemantics, ID, queryBody> 00180 { 00181 queryBody(QueryParserModuleSemantics& mgr): 00182 queryBody::base_type(mgr) { 00183 } 00184 }; 00185 }; 00186 00187 // create semantic handler for above semantic action 00188 // (needs to be in globally specializable struct space) 00189 template<> 00190 struct sem<QueryParserModuleSemantics::queryBody> 00191 { 00192 void operator()( 00193 QueryParserModuleSemantics& mgr, 00194 const boost::fusion::vector2< 00195 boost::optional<std::vector<ID> >, std::vector<ID> >& source, 00196 ID&) { // the target is not used 00197 if( !mgr.ctxdata.query.empty() ) { 00198 LOG(WARNING,"got more than one query, ignoring all but the first one!"); 00199 return; 00200 } 00201 00202 // remember the number of existentially quantified variables in the query 00203 // (for correct query answering, the computation of the possibly infinite universal model must not stop before its depth is greater or equal to this number) 00204 if (!!boost::fusion::at_c<0>(source)) { 00205 // count number of distinct variables 00206 std::set<ID> distinct; 00207 BOOST_FOREACH (ID v, boost::fusion::at_c<0>(source).get()) distinct.insert(v); 00208 mgr.ctx.config.setOption("LiberalSafetyNullFreezeCount", distinct.size()); 00209 } 00210 00211 // set query 00212 mgr.ctxdata.query = boost::fusion::at_c<1>(source); 00213 00214 // get variables/check groundness 00215 std::set<ID> vars; 00216 mgr.ctx.registry()->getVariablesInTuple(mgr.ctxdata.query, vars); 00217 mgr.ctxdata.ground = vars.empty(); 00218 DBGLOG(DBG,"found variables " << printset(vars) << " in query"); 00219 LOG(INFO,"got " << (mgr.ctxdata.ground?"ground":"nonground") << " query!"); 00220 00221 if( mgr.ctxdata.allWitnesses && !mgr.ctxdata.ground ) { 00222 LOG(WARNING,"--query-all is only useful for ground queries!"); 00223 } 00224 00225 // safety of the query is implicitly checked by checking safety 00226 // of the transformed rules 00227 WARNING("we should check query safety explicitly to get better error messages") 00228 } 00229 }; 00230 00231 namespace 00232 { 00233 00234 template<typename Iterator, typename Skipper> 00235 struct QueryParserModuleGrammarBase: 00236 // we derive from the original hex grammar 00237 // -> we can reuse its rules 00238 public HexGrammarBase<Iterator, Skipper> 00239 { 00240 typedef HexGrammarBase<Iterator, Skipper> Base; 00241 00242 QueryParserModuleSemantics& sem; 00243 00244 QueryParserModuleGrammarBase(QueryParserModuleSemantics& sem): 00245 Base(sem), 00246 sem(sem) { 00247 typedef QueryParserModuleSemantics Sem; 00248 query 00249 = ( 00250 // existential quantification 00251 -(qi::lit('+') >> Base::terms >> qi::lit(':')) >> 00252 (Base::bodyLiteral % qi::char_(',')) >> 00253 qi::lit('?') > 00254 qi::eps 00255 ) [ Sem::queryBody(sem) ]; 00256 00257 #ifdef BOOST_SPIRIT_DEBUG 00258 BOOST_SPIRIT_DEBUG_NODE(query); 00259 #endif 00260 } 00261 00262 qi::rule<Iterator, ID(), Skipper> query; 00263 }; 00264 00265 struct QueryParserModuleGrammar: 00266 QueryParserModuleGrammarBase<HexParserIterator, HexParserSkipper>, 00267 // required for interface 00268 // note: HexParserModuleGrammar = 00269 // boost::spirit::qi::grammar<HexParserIterator, HexParserSkipper> 00270 HexParserModuleGrammar 00271 { 00272 typedef QueryParserModuleGrammarBase<HexParserIterator, HexParserSkipper> GrammarBase; 00273 typedef HexParserModuleGrammar QiBase; 00274 00275 QueryParserModuleGrammar(QueryParserModuleSemantics& sem): 00276 GrammarBase(sem), 00277 QiBase(GrammarBase::query) { 00278 } 00279 }; 00280 typedef boost::shared_ptr<QueryParserModuleGrammar> 00281 QueryParserModuleGrammarPtr; 00282 00283 class QueryParserModule: 00284 public HexParserModule 00285 { 00286 public: 00287 // the semantics manager is stored/owned by this module! 00288 QueryParserModuleSemantics sem; 00289 // we also keep a shared ptr to the grammar module here 00290 QueryParserModuleGrammarPtr grammarModule; 00291 00292 QueryParserModule(ProgramCtx& ctx): 00293 HexParserModule(TOPLEVEL), 00294 sem(ctx) { 00295 LOG(INFO,"constructed QueryParserModule"); 00296 } 00297 00298 virtual HexParserModuleGrammarPtr createGrammarModule() { 00299 assert(!grammarModule && "for simplicity (storing only one grammarModule pointer) we currently assume this will be called only once .. should be no problem to extend"); 00300 grammarModule.reset(new QueryParserModuleGrammar(sem)); 00301 LOG(INFO,"created QueryParserModuleGrammar"); 00302 return grammarModule; 00303 } 00304 }; 00305 00306 } // anonymous namespace 00307 00308 00309 // create parser modules that extend and the basic hex grammar 00310 // this parser also stores the query information into the plugin 00311 std::vector<HexParserModulePtr> 00312 QueryPlugin::createParserModules(ProgramCtx& ctx) 00313 { 00314 DBGLOG(DBG,"QueryPlugin::createParserModules()"); 00315 std::vector<HexParserModulePtr> ret; 00316 00317 QueryPlugin::CtxData& ctxdata = ctx.getPluginData<QueryPlugin>(); 00318 if( ctxdata.enabled ) { 00319 ret.push_back(HexParserModulePtr( 00320 new QueryParserModule(ctx))); 00321 } 00322 00323 return ret; 00324 } 00325 00326 00327 namespace 00328 { 00329 00330 typedef QueryPlugin::CtxData CtxData; 00331 00332 class QueryAdderRewriter: 00333 public PluginRewriter 00334 { 00335 public: 00336 QueryAdderRewriter() {} 00337 virtual ~QueryAdderRewriter() {} 00338 00339 virtual void rewrite(ProgramCtx& ctx); 00340 }; 00341 00342 void QueryAdderRewriter::rewrite(ProgramCtx& ctx) { 00343 DBGLOG_SCOPE(DBG,"query_rewrite",false); 00344 DBGLOG(DBG,"= QueryAdderRewriter::rewrite"); 00345 00346 QueryPlugin::CtxData& ctxdata = ctx.getPluginData<QueryPlugin>(); 00347 assert(ctxdata.enabled && "this rewriter should only be used " 00348 "if the plugin is enabled"); 00349 00350 RegistryPtr reg = ctx.registry(); 00351 assert(reg); 00352 00353 if( ctxdata.query.empty() ) 00354 throw FatalError("query mode enabled, but got no query!"); 00355 00356 // convert query 00357 if( ctxdata.mode == CtxData::BRAVE && ctxdata.ground ) { 00358 // from query a_1,...,a_j,not a_{j+1},...,not a_n 00359 // create constraints 00360 // :- not a_i. for 1 <= i <= j 00361 // :- a_i. for j+1 <= i <= n 00362 // then all answer sets are positive witnesses of the ground query 00363 00364 assert(!ctxdata.query.empty()); 00365 BOOST_FOREACH(ID idl, ctxdata.query) { 00366 Rule r( 00367 ID::MAINKIND_RULE | 00368 ID::SUBKIND_RULE_CONSTRAINT | 00369 ID::PROPERTY_AUX); 00370 ID negated_idl(ID::literalFromAtom( 00371 ID::atomFromLiteral(idl), 00372 !idl.isNaf())); 00373 r.body.push_back(negated_idl); 00374 00375 ID idcon = reg->storeRule(r); 00376 ctx.idb.push_back(idcon); 00377 DBGLOG(DBG,"created aux constraint '" << 00378 printToString<RawPrinter>(idcon, reg) << "'"); 00379 } 00380 } 00381 else if( ctxdata.mode == CtxData::CAUTIOUS && ctxdata.ground ) { 00382 // from query a_1,...,a_j,not a_{j+1},...,not a_n 00383 // create constraint 00384 // :- a_1,...,a_j,not a_{j+1},...,not a_n. 00385 // then all answer sets are negative witnesses of the ground query 00386 00387 assert(!ctxdata.query.empty()); 00388 Rule r( 00389 ID::MAINKIND_RULE | 00390 ID::SUBKIND_RULE_CONSTRAINT | 00391 ID::PROPERTY_AUX); 00392 r.body = ctxdata.query; 00393 00394 ID idcon = reg->storeRule(r); 00395 ctx.idb.push_back(idcon); 00396 DBGLOG(DBG,"created aux constraint '" << 00397 printToString<RawPrinter>(idcon, reg) << "'"); 00398 } 00399 else if( !ctxdata.ground ) { 00400 // from query a_1,...,a_j,not a_{j+1},...,not a_n 00401 // with variables X_1,...,X_k 00402 // create rule 00403 // aux[q0](X_1,...,X_k) :- a_1,...,a_j,not a_{j+1},...,not a_n. 00404 00405 // create auxiliary 00406 ctxdata.varAuxPred = reg->getAuxiliaryConstantSymbol('q', ID(0,0)); 00407 00408 // get variables 00409 std::set<ID> vars; 00410 reg->getVariablesInTuple(ctxdata.query, vars); 00411 assert(!vars.empty() && "nonground queries contain at least one variable"); 00412 00413 // register variables and build var aux predicate 00414 OrdinaryAtom auxHead(ID::MAINKIND_ATOM | 00415 ID::SUBKIND_ATOM_ORDINARYN | ID::PROPERTY_AUX); 00416 auxHead.tuple.push_back(ctxdata.varAuxPred); 00417 assert(ctxdata.variableIDs.empty()); 00418 BOOST_FOREACH(ID idvar, vars) { 00419 auxHead.tuple.push_back(idvar); 00420 ctxdata.variableIDs.push_back(idvar); 00421 } 00422 ID varAuxHeadId = reg->storeOrdinaryNAtom(auxHead); 00423 DBGLOG(DBG,"stored auxiliary query head " << 00424 printToString<RawPrinter>(varAuxHeadId, reg)); 00425 00426 // add auxiliary rule with variables 00427 Rule varAuxRule(ID::MAINKIND_RULE | 00428 ID::SUBKIND_RULE_REGULAR | ID::PROPERTY_AUX); 00429 WARNING("TODO extatom flag in rule") 00430 varAuxRule.head.push_back(varAuxHeadId); 00431 varAuxRule.body = ctxdata.query; 00432 ID varAuxRuleId = reg->storeRule(varAuxRule); 00433 ctx.idb.push_back(varAuxRuleId); 00434 LOG(DBG,"added auxiliary rule " << 00435 printToString<RawPrinter>(varAuxRuleId, reg)); 00436 00437 if( ctxdata.mode == CtxData::BRAVE ) { 00438 // create rule 00439 // aux[q1] :- aux(Q)(X_1,...,X_k). 00440 // create constraint 00441 // :- not aux[q1]. 00442 // then all answer sets are positive witnesses of the nonground query 00443 // and facts aux[q0] in the respective model gives all bravely true substitutions 00444 00445 // create auxiliary 00446 ctxdata.novarAuxPred = reg->getAuxiliaryConstantSymbol('q', ID(0,1)); 00447 00448 // build novar aux predicate 00449 OrdinaryAtom nvauxHead(ID::MAINKIND_ATOM | 00450 ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX); 00451 nvauxHead.tuple.push_back(ctxdata.novarAuxPred); 00452 ID novarAuxHeadId = reg->storeOrdinaryGAtom(nvauxHead); 00453 DBGLOG(DBG,"stored auxiliary query head " << 00454 printToString<RawPrinter>(novarAuxHeadId, reg)); 00455 00456 // add auxiliary rule without variables 00457 Rule novarAuxRule(ID::MAINKIND_RULE | 00458 ID::SUBKIND_RULE_REGULAR | ID::PROPERTY_AUX); 00459 novarAuxRule.head.push_back(novarAuxHeadId); 00460 novarAuxRule.body.push_back(ID::literalFromAtom(varAuxHeadId, false)); 00461 ID novarAuxRuleId = reg->storeRule(novarAuxRule); 00462 ctx.idb.push_back(novarAuxRuleId); 00463 LOG(DBG,"added auxiliary rule " << 00464 printToString<RawPrinter>(novarAuxRuleId, reg)); 00465 00466 // add auxiliary constraint 00467 Rule auxConstraint(ID::MAINKIND_RULE | 00468 ID::SUBKIND_RULE_CONSTRAINT | ID::PROPERTY_AUX); 00469 auxConstraint.body.push_back(ID::literalFromAtom(novarAuxHeadId, true)); 00470 ID auxConstraintId = reg->storeRule(auxConstraint); 00471 ctx.idb.push_back(auxConstraintId); 00472 LOG(DBG,"added auxiliary constraint " << 00473 printToString<RawPrinter>(auxConstraintId, reg)); 00474 } 00475 else if( ctxdata.mode == CtxData::CAUTIOUS ) { 00476 // intersect all answer sets, 00477 // facts aux[q0] in the resulting model gives all cautiously true substitutions 00478 } 00479 else { 00480 assert("this case should never happen"); 00481 } 00482 } 00483 else { 00484 assert("this case should never happen"); 00485 } 00486 } 00487 00488 } // anonymous namespace 00489 00490 00491 // rewrite program by adding auxiliary query rules 00492 PluginRewriterPtr QueryPlugin::createRewriter(ProgramCtx& ctx) 00493 { 00494 QueryPlugin::CtxData& ctxdata = ctx.getPluginData<QueryPlugin>(); 00495 if( !ctxdata.enabled ) 00496 return PluginRewriterPtr(); 00497 00498 return PluginRewriterPtr(new QueryAdderRewriter); 00499 } 00500 00501 00502 namespace 00503 { 00504 00505 class WitnessPrinterCallback: 00506 public ModelCallback 00507 { 00508 public: 00509 WitnessPrinterCallback( 00510 const std::string& message, 00511 bool abortAfterFirstWitness); 00512 virtual ~WitnessPrinterCallback() {} 00513 00514 virtual bool operator()(AnswerSetPtr model); 00515 bool gotOne() const { return gotOneModel; } 00516 00517 protected: 00518 std::string message; 00519 bool abortAfterFirst; 00520 bool gotOneModel; 00521 }; 00522 typedef boost::shared_ptr<WitnessPrinterCallback> WitnessPrinterCallbackPtr; 00523 00524 WitnessPrinterCallback::WitnessPrinterCallback( 00525 const std::string& message, 00526 bool abortAfterFirstWitness): 00527 message(message), 00528 abortAfterFirst(abortAfterFirstWitness), 00529 gotOneModel(false) { 00530 } 00531 00532 WARNING("TODO perhaps derive from AnswerSetPrinterCallback?") 00533 bool WitnessPrinterCallback::operator()( 00534 AnswerSetPtr model) { 00535 RegistryPtr reg = model->interpretation->getRegistry(); 00536 const Interpretation::Storage& bits = model->interpretation->getStorage(); 00537 std::ostream& o = std::cout; 00538 00539 o << message; 00540 o << '{'; 00541 Interpretation::Storage::enumerator it = bits.first(); 00542 if( it != bits.end() ) { 00543 bool gotOutput = 00544 reg->printAtomForUser(o, *it); 00545 it++; 00546 for(; it != bits.end(); ++it) { 00547 if( gotOutput ) 00548 o << ','; 00549 gotOutput = 00550 reg->printAtomForUser(o, *it); 00551 } 00552 } 00553 o << '}' << std::endl; 00554 gotOneModel = true; 00555 if( abortAfterFirst ) 00556 return false; 00557 else 00558 return true; 00559 } 00560 00561 class VerdictPrinterCallback: 00562 public FinalCallback 00563 { 00564 public: 00565 VerdictPrinterCallback( 00566 const std::string& message, WitnessPrinterCallbackPtr wprinter); 00567 virtual ~VerdictPrinterCallback() {} 00568 00569 virtual void operator()(); 00570 00571 protected: 00572 std::string message; 00573 WitnessPrinterCallbackPtr wprinter; 00574 }; 00575 00576 VerdictPrinterCallback::VerdictPrinterCallback( 00577 const std::string& message, 00578 WitnessPrinterCallbackPtr wprinter): 00579 message(message), 00580 wprinter(wprinter) { 00581 } 00582 00583 void VerdictPrinterCallback::operator()() { 00584 assert(!!wprinter); 00585 // if no model was returned, we have a message to emit 00586 if( !wprinter->gotOne() ) { 00587 std::cout << message << std::endl; 00588 } 00589 } 00590 00591 // gets all auxiliary substitution atoms from the model 00592 // substitutes into the query 00593 // outputs the query (one line per substitution) 00594 // (this is used in brave mode and cautious mode derives from this) 00595 class QuerySubstitutionPrinterCallback: 00596 public ModelCallback 00597 { 00598 public: 00599 QuerySubstitutionPrinterCallback( 00600 RegistryPtr reg, const CtxData& ctxdata); 00601 virtual ~QuerySubstitutionPrinterCallback() {} 00602 00603 virtual bool operator()(AnswerSetPtr model); 00604 00605 protected: 00606 virtual void substituteIntoQueryAndPrint( 00607 std::ostream& o, RegistryPtr reg, const Tuple& substitution) const; 00608 virtual void printAllSubstitutions( 00609 std::ostream& o, InterpretationPtr interpretation); 00610 00611 protected: 00612 const CtxData& ctxdata; 00613 00614 // incrementally managed ground atom projection helper 00615 PredicateMask mask; 00616 00617 // store already printed substitutions to avoid duplicate prints 00618 std::set<Tuple> printedSubstitutions; 00619 00620 // "expanded query" cached 00621 std::vector<bool> querycacheNaf; 00622 std::vector<OrdinaryAtom> querycache; 00623 }; 00624 00625 QuerySubstitutionPrinterCallback::QuerySubstitutionPrinterCallback( 00626 RegistryPtr reg, 00627 const CtxData& ctxdata): 00628 ctxdata(ctxdata) { 00629 mask.setRegistry(reg); 00630 mask.addPredicate(ctxdata.varAuxPred); 00631 00632 // create query cache 00633 BOOST_FOREACH(ID litid, ctxdata.query) { 00634 querycacheNaf.push_back(litid.isNaf()); 00635 querycache.push_back(reg->lookupOrdinaryAtom(litid)); 00636 } 00637 } 00638 00639 bool QuerySubstitutionPrinterCallback::operator()( 00640 AnswerSetPtr model) { 00641 DBGLOG_SCOPE(DBG,"qspc",false); 00642 DBGLOG(DBG,"= QuerySubstitutionPrinterCallback::operator()"); 00643 00644 typedef Interpretation::Storage Storage; 00645 Storage& bits = model->interpretation->getStorage(); 00646 RegistryPtr reg = model->interpretation->getRegistry(); 00647 00648 // extract interesting atoms 00649 mask.updateMask(); 00650 // project model (we destroy the original answer set in place!) 00651 bits &= mask.mask()->getStorage(); 00652 DBGLOG(DBG,"projected model to " << *model->interpretation); 00653 00654 printAllSubstitutions(std::cout, model->interpretation); 00655 00656 // never abort 00657 return true; 00658 } 00659 00660 void QuerySubstitutionPrinterCallback:: 00661 substituteIntoQueryAndPrint( 00662 std::ostream& o, RegistryPtr reg, const Tuple& substitution) const 00663 { 00664 // prepare substitution map 00665 std::map<ID,ID> mapper; 00666 assert(substitution.size() == ctxdata.variableIDs.size()); 00667 for(unsigned u = 0; u < substitution.size(); ++u) { 00668 mapper[ctxdata.variableIDs[u]] = substitution[u]; 00669 } 00670 00671 // substitute and print 00672 RawPrinter p(o, reg); 00673 assert(querycacheNaf.size() == querycache.size()); 00674 assert(!querycache.empty()); 00675 o << "{"; 00676 bool firstPrinted = true; 00677 for(unsigned u = 0; u < querycache.size(); ++u) { 00678 if( querycacheNaf[u] ) { 00679 // do not print naf literals in query 00680 //o << "not "; 00681 continue; 00682 } 00683 if( firstPrinted ) { 00684 firstPrinted = false; 00685 } 00686 else { 00687 o << ", "; 00688 } 00689 const Tuple& atom = querycache[u].tuple; 00690 assert(!atom.empty()); 00691 assert(!atom.front().isVariableTerm()); 00692 p.print(atom.front()); 00693 if( atom.size() > 1 ) { 00694 // copy body 00695 Tuple atombody(atom.begin()+1, atom.end()); 00696 00697 // substitute 00698 for(Tuple::iterator it = atombody.begin(); 00699 it != atombody.end(); ++it) { 00700 if( it->isVariableTerm() ) { 00701 assert(mapper.count(*it) == 1 && 00702 "variable in query must be substituted!"); 00703 *it = mapper[*it]; 00704 } 00705 } 00706 00707 // print 00708 o << "("; 00709 p.printmany(atombody,","); 00710 o << ")"; 00711 } 00712 } 00713 o << "}"; 00714 } 00715 00716 void QuerySubstitutionPrinterCallback:: 00717 printAllSubstitutions( 00718 std::ostream& o, InterpretationPtr interpretation) { 00719 typedef Interpretation::Storage Storage; 00720 RegistryPtr reg = interpretation->getRegistry(); 00721 Storage& bits = interpretation->getStorage(); 00722 for(Storage::enumerator it = bits.first(); 00723 it != bits.end(); ++it) { 00724 // build substitution tuple 00725 const OrdinaryAtom& ogatom = reg->ogatoms.getByAddress(*it); 00726 DBGLOG(DBG,"got auxiliary " << ogatom.text); 00727 assert(ogatom.tuple.size() > 1); 00728 Tuple subst(ogatom.tuple.begin()+1, ogatom.tuple.end()); 00729 assert(!subst.empty()); 00730 00731 // discard duplicates 00732 if( printedSubstitutions.find(subst) != printedSubstitutions.end() ) { 00733 LOG(DBG,"discarded duplicate substitution from auxiliary atom " << ogatom.text); 00734 continue; 00735 } 00736 00737 // add and print substitution 00738 printedSubstitutions.insert(subst); 00739 substituteIntoQueryAndPrint(o, reg, subst); 00740 o << std::endl; 00741 } 00742 } 00743 00744 // first model: project auxiliary substitution atoms into cached interpretation 00745 // other models: intersect model with cached interpretation 00746 // prints substitutions in projected interpretation to STDERR 00747 // (this is used in cautious mode) 00748 class IntersectedQuerySubstitutionPrinterCallback: 00749 public QuerySubstitutionPrinterCallback 00750 { 00751 public: 00752 IntersectedQuerySubstitutionPrinterCallback( 00753 RegistryPtr reg, const CtxData& ctxdata, 00754 bool printPreliminaryModels); 00755 virtual ~IntersectedQuerySubstitutionPrinterCallback() {} 00756 00757 virtual bool operator()(AnswerSetPtr model); 00758 00759 // print result after it is clear that no more models follow 00760 virtual void printFinalAnswer(); 00761 00762 protected: 00763 InterpretationPtr cachedInterpretation; 00764 bool printPreliminaryModels; 00765 }; 00766 typedef boost::shared_ptr<IntersectedQuerySubstitutionPrinterCallback> 00767 IntersectedQuerySubstitutionPrinterCallbackPtr; 00768 00769 IntersectedQuerySubstitutionPrinterCallback::IntersectedQuerySubstitutionPrinterCallback( 00770 RegistryPtr reg, 00771 const CtxData& ctxdata, 00772 bool printPreliminaryModels): 00773 QuerySubstitutionPrinterCallback(reg, ctxdata), 00774 // do not create it here! 00775 cachedInterpretation(), 00776 printPreliminaryModels(printPreliminaryModels) { 00777 } 00778 00779 bool IntersectedQuerySubstitutionPrinterCallback::operator()( 00780 AnswerSetPtr model) { 00781 DBGLOG_SCOPE(DBG,"iqspc",false); 00782 DBGLOG(DBG,"= IntersectedQuerySubstitutionPrinterCallback::operator()"); 00783 00784 typedef Interpretation::Storage Storage; 00785 RegistryPtr reg = model->interpretation->getRegistry(); 00786 00787 bool changed = false; 00788 if( !cachedInterpretation ) { 00789 // this is the first model 00790 DBGLOG(DBG,"got initial model " << *model->interpretation); 00791 00792 // -> copy it 00793 cachedInterpretation.reset(new Interpretation(*model->interpretation)); 00794 changed = true; 00795 00796 Storage& bits = cachedInterpretation->getStorage(); 00797 00798 // extract interesting atoms 00799 mask.updateMask(); 00800 // project model (we destroy the original answer set in place!) 00801 bits &= mask.mask()->getStorage(); 00802 DBGLOG(DBG,"projected initial model to " << *cachedInterpretation); 00803 } 00804 else { 00805 // this is a subsequent model 00806 DBGLOG(DBG,"got subsequent model " << *model->interpretation); 00807 00808 // intersect with new model 00809 bm::id_t oldBits = cachedInterpretation->getStorage().count(); 00810 cachedInterpretation->getStorage() &= model->interpretation->getStorage(); 00811 bm::id_t newBits = cachedInterpretation->getStorage().count(); 00812 changed = (newBits != oldBits); 00813 DBGLOG(DBG,"projected cached interpretation to " << *cachedInterpretation << 00814 (changed?"(changed)":"(unchanged)")); 00815 } 00816 00817 assert(!!cachedInterpretation); 00818 00819 if( changed && printPreliminaryModels ) { 00820 // display preliminary set of substitutions (on stderr) 00821 std::cerr << "preliminary cautious query answers:" << std::endl; 00822 00823 // reset duplicate elimination set 00824 printedSubstitutions.clear(); 00825 00826 printAllSubstitutions(std::cerr, cachedInterpretation); 00827 } 00828 00829 // abort iff cached interpretation contains no bits -> no more substitutions cautiously entailed 00830 if( cachedInterpretation->getStorage().none() ) { 00831 // abort 00832 return false; 00833 } 00834 else { 00835 // do not abort 00836 return true; 00837 } 00838 } 00839 00840 // print result after it is clear that no more models follow 00841 void IntersectedQuerySubstitutionPrinterCallback:: 00842 printFinalAnswer() { 00843 if( !!cachedInterpretation ) { 00844 // reset duplicate elimination set 00845 printedSubstitutions.clear(); 00846 00847 // print this to stderr s.t. stdout output contains only models 00848 if( printPreliminaryModels ) 00849 std::cerr << "final cautious query answers:" << std::endl; 00850 00851 // print result 00852 printAllSubstitutions(std::cout, cachedInterpretation); 00853 } 00854 // print nothing if final answer is "no cautiously entailed substitutions" 00855 } 00856 00857 class CautiousVerdictPrinterCallback: 00858 public FinalCallback 00859 { 00860 public: 00861 CautiousVerdictPrinterCallback( 00862 IntersectedQuerySubstitutionPrinterCallbackPtr iqsprinter); 00863 virtual ~CautiousVerdictPrinterCallback() {} 00864 00865 virtual void operator()(); 00866 00867 protected: 00868 IntersectedQuerySubstitutionPrinterCallbackPtr iqsprinter; 00869 }; 00870 00871 CautiousVerdictPrinterCallback::CautiousVerdictPrinterCallback( 00872 IntersectedQuerySubstitutionPrinterCallbackPtr iqsprinter): 00873 iqsprinter(iqsprinter) { 00874 } 00875 00876 void CautiousVerdictPrinterCallback::operator()() { 00877 assert(!!iqsprinter); 00878 // fully delegate printing to iqsprinter 00879 iqsprinter->printFinalAnswer(); 00880 } 00881 00882 } // anonymous namespace 00883 00884 00885 // change model callback and register final callback 00886 void QueryPlugin::setupProgramCtx(ProgramCtx& ctx) 00887 { 00888 QueryPlugin::CtxData& ctxdata = ctx.getPluginData<QueryPlugin>(); 00889 if( !ctxdata.enabled ) 00890 return; 00891 00892 RegistryPtr reg = ctx.registry(); 00893 assert(!!reg); 00894 00895 if( ctxdata.ground ) { 00896 // create messages 00897 std::string modelmsg, finalmsg; 00898 { 00899 std::stringstream msgs; 00900 RawPrinter pr(msgs, reg); 00901 pr.printmany(ctxdata.query, ", "); 00902 msgs << " is "; 00903 switch(ctxdata.mode) { 00904 case CtxData::BRAVE: 00905 msgs << "bravely "; 00906 modelmsg = msgs.str() + "true, evidenced by "; 00907 finalmsg = msgs.str() + "false."; 00908 break; 00909 case CtxData::CAUTIOUS: 00910 msgs << "cautiously "; 00911 modelmsg = msgs.str() + "false, evidenced by "; 00912 finalmsg = msgs.str() + "true."; 00913 break; 00914 default: 00915 assert("unknown querying mode!"); 00916 } 00917 } 00918 00919 WitnessPrinterCallbackPtr wprinter( 00920 new WitnessPrinterCallback( 00921 modelmsg, !ctxdata.allWitnesses)); 00922 FinalCallbackPtr fprinter( 00923 new VerdictPrinterCallback(finalmsg, wprinter)); 00924 WARNING("here we could try to only remove the default answer set printer") 00925 ctx.modelCallbacks.clear(); 00926 ctx.modelCallbacks.push_back(wprinter); 00927 ctx.finalCallbacks.push_back(fprinter); 00928 } 00929 else { 00930 switch(ctxdata.mode) { 00931 case CtxData::BRAVE: 00932 { 00933 ModelCallbackPtr qsprinter(new QuerySubstitutionPrinterCallback(reg, ctxdata)); 00934 WARNING("here we could try to only remove the default answer set printer") 00935 ctx.modelCallbacks.clear(); 00936 ctx.modelCallbacks.push_back(qsprinter); 00937 } 00938 break; 00939 case CtxData::CAUTIOUS: 00940 { 00941 bool printPreliminaryModels = !ctx.config.getOption("Silent"); 00942 IntersectedQuerySubstitutionPrinterCallbackPtr iqsprinter( 00943 new IntersectedQuerySubstitutionPrinterCallback( 00944 reg, ctxdata, printPreliminaryModels)); 00945 WARNING("here we could try to only remove the default answer set printer") 00946 ctx.modelCallbacks.clear(); 00947 ctx.modelCallbacks.push_back(iqsprinter); 00948 FinalCallbackPtr fprinter( 00949 new CautiousVerdictPrinterCallback(iqsprinter)); 00950 ctx.finalCallbacks.push_back(fprinter); 00951 } 00952 break; 00953 default: 00954 assert("unknown querying mode!"); 00955 } 00956 } 00957 } 00958 00959 00960 DLVHEX_NAMESPACE_END 00961 00962 // this would be the code to use this plugin as a "real" plugin in a .so file 00963 // but we directly use it in dlvhex.cpp 00964 #if 0 00965 QueryPlugin theQueryPlugin; 00966 00967 // return plain C type s.t. all compilers and linkers will like this code 00968 extern "C" 00969 void * PLUGINIMPORTFUNCTION() 00970 { 00971 return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theQueryPlugin); 00972 } 00973 #endif 00974 00975 00976 // vim:expandtab:ts=4:sw=4: 00977 // mode: C++ 00978 // End: