dlvhex  2.5.0
src/QueryPlugin.cpp
Go to the documentation of this file.
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: