dlvhex  2.5.0
src/StrongNegationPlugin.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/StrongNegationPlugin.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/PredicateMask.h"
00047 #include "dlvhex2/Logger.h"
00048 #include "dlvhex2/HexParser.h"
00049 #include "dlvhex2/HexParserModule.h"
00050 #include "dlvhex2/HexGrammar.h"
00051 
00052 #include <boost/algorithm/string/predicate.hpp>
00053 #include <boost/lexical_cast.hpp>
00054 
00055 DLVHEX_NAMESPACE_BEGIN
00056 
00057 namespace spirit = boost::spirit;
00058 namespace qi = boost::spirit::qi;
00059 
00060 StrongNegationPlugin::CtxData::CtxData():
00061 enabled(false),
00062 negPredicateArities()
00063 {
00064 }
00065 
00066 
00067 StrongNegationPlugin::StrongNegationPlugin():
00068 PluginInterface()
00069 {
00070     setNameVersion("dlvhex-strongnegationplugin[internal]", 2, 0, 0);
00071 }
00072 
00073 
00074 StrongNegationPlugin::~StrongNegationPlugin()
00075 {
00076 }
00077 
00078 
00079 // output help message for this plugin
00080 void StrongNegationPlugin::printUsage(std::ostream& o) const
00081 {
00082     //    123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-
00083     o << "     --strongnegation-enable[=true,false]" << std::endl
00084         << "                      Enable or disable strong negation plugin (default is enabled)." << std::endl;
00085 }
00086 
00087 
00088 // accepted options: --strongnegation-enable
00089 //
00090 // processes options for this plugin, and removes recognized options from pluginOptions
00091 // (do not free the pointers, the const char* directly come from argv)
00092 void StrongNegationPlugin::processOptions(
00093 std::list<const char*>& pluginOptions,
00094 ProgramCtx& ctx)
00095 {
00096     StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>();
00097     ctxdata.enabled = true;
00098 
00099     typedef std::list<const char*>::iterator Iterator;
00100     Iterator it;
00101     WARNING("create (or reuse, maybe from potassco?) cmdline option processing facility")
00102         it = pluginOptions.begin();
00103     while( it != pluginOptions.end() ) {
00104         bool processed = false;
00105         const std::string str(*it);
00106         if( boost::starts_with(str, "--strongnegation-enable" ) ) {
00107             std::string m = str.substr(std::string("--strongnegation-enable").length());
00108             if (m == "" || m == "=true") {
00109                 ctxdata.enabled = true;
00110             }
00111             else if (m == "=false") {
00112                 ctxdata.enabled = false;
00113             }
00114             else {
00115                 std::stringstream ss;
00116                 ss << "Unknown --strongnegation-enable option: " << m;
00117                 throw PluginError(ss.str());
00118             }
00119             processed = true;
00120         }
00121 
00122         if( processed ) {
00123             // return value of erase: element after it, maybe end()
00124             DBGLOG(DBG,"StrongNegationPlugin successfully processed option " << str);
00125             it = pluginOptions.erase(it);
00126         }
00127         else {
00128             it++;
00129         }
00130     }
00131 }
00132 
00133 
00134 class StrongNegationParserModuleSemantics:
00135 public HexGrammarSemantics
00136 {
00137     public:
00138         StrongNegationPlugin::CtxData& ctxdata;
00139 
00140     public:
00141         StrongNegationParserModuleSemantics(ProgramCtx& ctx):
00142         HexGrammarSemantics(ctx),
00143         ctxdata(ctx.getPluginData<StrongNegationPlugin>()) {
00144         }
00145 
00146         // use SemanticActionBase to redirect semantic action call into globally
00147         // specializable sem<T> struct space
00148         struct stronglyNegatedPrefixAtom:
00149         SemanticActionBase<StrongNegationParserModuleSemantics, ID, stronglyNegatedPrefixAtom>
00150         {
00151             stronglyNegatedPrefixAtom(StrongNegationParserModuleSemantics& mgr):
00152             stronglyNegatedPrefixAtom::base_type(mgr) {
00153             }
00154         };
00155 };
00156 
00157 // create semantic handler for above semantic action
00158 // (needs to be in globally specializable struct space)
00159 template<>
00160 struct sem<StrongNegationParserModuleSemantics::stronglyNegatedPrefixAtom>
00161 {
00162     void createAtom(RegistryPtr reg, OrdinaryAtom& atom, ID& target) {
00163         // groundness
00164         DBGLOG(DBG,"checking groundness of tuple " << printrange(atom.tuple));
00165         IDKind kind = 0;
00166         BOOST_FOREACH(const ID& id, atom.tuple) {
00167             kind |= id.kind;
00168             // make this sure to make the groundness check work
00169             // (if we add "builtin constant terms" like #supremum we might have to change the above statement)
00170             assert((id.kind & ID::SUBKIND_MASK) != ID::SUBKIND_TERM_BUILTIN);
00171         }
00172         const bool ground = !(kind & ID::SUBKIND_TERM_VARIABLE);
00173         if( ground ) {
00174             atom.kind |= ID::SUBKIND_ATOM_ORDINARYG;
00175             target = reg->storeOrdinaryGAtom(atom);
00176         }
00177         else {
00178             atom.kind |= ID::SUBKIND_ATOM_ORDINARYN;
00179             target = reg->storeOrdinaryNAtom(atom);
00180         }
00181         DBGLOG(DBG,"stored atom " << atom << " which got id " << target);
00182     }
00183 
00184     void operator()(
00185         StrongNegationParserModuleSemantics& mgr,
00186         const boost::fusion::vector2<
00187         dlvhex::ID,
00188         boost::optional<boost::optional<std::vector<dlvhex::ID> > >
00189         >& source,
00190     ID& target) {
00191         typedef StrongNegationPlugin::CtxData::PredicateArityMap PredicateArityMap;
00192 
00193         RegistryPtr reg = mgr.ctx.registry();
00194 
00195         // strong negation is always present here!
00196 
00197         // predicate
00198         const ID& idpred = boost::fusion::at_c<0>(source);
00199 
00200         // create/get aux constant for idpred
00201         const ID idnegpred = reg->getAuxiliaryConstantSymbol('s', idpred);
00202 
00203         // build atom with auxiliary (SUBKIND is initialized by createAtom())
00204         OrdinaryAtom atom(ID::MAINKIND_ATOM | ID::PROPERTY_AUX);
00205         atom.tuple.push_back(idnegpred);
00206 
00207         // arguments
00208         if( (!!boost::fusion::at_c<1>(source)) &&
00209         (!!(boost::fusion::at_c<1>(source).get())) ) {
00210             const Tuple& tuple = boost::fusion::at_c<1>(source).get().get();
00211             atom.tuple.insert(atom.tuple.end(), tuple.begin(), tuple.end());
00212         }
00213 
00214         // store predicate with arity (ensure each predicate is used with only one arity)
00215         PredicateArityMap::const_iterator it =
00216             mgr.ctxdata.negPredicateArities.find(idpred);
00217         if( it == mgr.ctxdata.negPredicateArities.end() ) {
00218             // store as new
00219             mgr.ctxdata.negToPos[idnegpred] = idpred;
00220         }
00221         DBGLOG(DBG,"got strongly negated predicate " <<
00222             printToString<RawPrinter>(idpred, reg) << "/" <<
00223             idpred << " with arity " << atom.tuple.size() - 1);
00224         mgr.ctxdata.negPredicateArities[idpred].insert(atom.tuple.size() - 1);
00225 
00226         // create atom
00227         createAtom(reg, atom, target);
00228     }
00229 };
00230 
00231 namespace
00232 {
00233 
00234     template<typename Iterator, typename Skipper>
00235         struct StrongNegationParserModuleGrammarBase:
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         StrongNegationParserModuleSemantics& sem;
00243 
00244         StrongNegationParserModuleGrammarBase(StrongNegationParserModuleSemantics& sem):
00245         Base(sem),
00246         sem(sem) {
00247             typedef StrongNegationParserModuleSemantics Sem;
00248             stronglyNegatedPrefixAtom
00249                 = (
00250                 qi::lit('-') >> Base::classicalAtomPredicate >>
00251                 -(qi::lit('(') > -Base::terms >> qi::lit(')')) > qi::eps
00252                 ) [ Sem::stronglyNegatedPrefixAtom(sem) ];
00253 
00254             #ifdef BOOST_SPIRIT_DEBUG
00255             BOOST_SPIRIT_DEBUG_NODE(stronglyNegatedPrefixAtom);
00256             #endif
00257         }
00258 
00259         qi::rule<Iterator, ID(), Skipper> stronglyNegatedPrefixAtom;
00260     };
00261 
00262     struct StrongNegationParserModuleGrammar:
00263     StrongNegationParserModuleGrammarBase<HexParserIterator, HexParserSkipper>,
00264     // required for interface
00265     // note: HexParserModuleGrammar =
00266     //       boost::spirit::qi::grammar<HexParserIterator, HexParserSkipper>
00267         HexParserModuleGrammar
00268     {
00269         typedef StrongNegationParserModuleGrammarBase<HexParserIterator, HexParserSkipper> GrammarBase;
00270         typedef HexParserModuleGrammar QiBase;
00271 
00272         StrongNegationParserModuleGrammar(StrongNegationParserModuleSemantics& sem):
00273         GrammarBase(sem),
00274         QiBase(GrammarBase::stronglyNegatedPrefixAtom) {
00275         }
00276     };
00277     typedef boost::shared_ptr<StrongNegationParserModuleGrammar>
00278         StrongNegationParserModuleGrammarPtr;
00279 
00280     // moduletype = HexParserModule::BODYATOM
00281     // moduletype = HexParserModule::HEADATOM
00282     template<enum HexParserModule::Type moduletype>
00283     class StrongNegationParserModule:
00284     public HexParserModule
00285     {
00286         public:
00287             // the semantics manager is stored/owned by this module!
00288             StrongNegationParserModuleSemantics sem;
00289             // we also keep a shared ptr to the grammar module here
00290             StrongNegationParserModuleGrammarPtr grammarModule;
00291 
00292             StrongNegationParserModule(ProgramCtx& ctx):
00293             HexParserModule(moduletype),
00294             sem(ctx) {
00295                 LOG(INFO,"constructed StrongNegationParserModule");
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 StrongNegationParserModuleGrammar(sem));
00301                 LOG(INFO,"created StrongNegationParserModuleGrammar");
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 StrongNegationPlugin::createParserModules(ProgramCtx& ctx)
00313 {
00314     DBGLOG(DBG,"StrongNegationPlugin::createParserModules()");
00315     std::vector<HexParserModulePtr> ret;
00316 
00317     StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>();
00318     if( ctxdata.enabled ) {
00319         ret.push_back(HexParserModulePtr(
00320             new StrongNegationParserModule<HexParserModule::BODYATOM>(ctx)));
00321         ret.push_back(HexParserModulePtr(
00322             new StrongNegationParserModule<HexParserModule::HEADATOM>(ctx)));
00323     }
00324 
00325     return ret;
00326 }
00327 
00328 
00329 namespace
00330 {
00331 
00332     typedef StrongNegationPlugin::CtxData CtxData;
00333 
00334     class StrongNegationConstraintAdder:
00335     public PluginRewriter
00336     {
00337         public:
00338             StrongNegationConstraintAdder() {}
00339             virtual ~StrongNegationConstraintAdder() {}
00340 
00341             virtual void rewrite(ProgramCtx& ctx);
00342     };
00343 
00344     void StrongNegationConstraintAdder::rewrite(ProgramCtx& ctx) {
00345         typedef StrongNegationPlugin::CtxData::PredicateArityMap PredicateArityMap;
00346 
00347         DBGLOG_SCOPE(DBG,"neg_rewr",false);
00348         DBGLOG(DBG,"= StrongNegationConstraintAdder::rewrite");
00349 
00350         StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>();
00351         assert(ctxdata.enabled && "this rewriter should only be used "
00352             "if the plugin is enabled");
00353 
00354         RegistryPtr reg = ctx.registry();
00355         assert(reg);
00356         PredicateArityMap::const_iterator it;
00357         for(it = ctxdata.negPredicateArities.begin();
00358         it != ctxdata.negPredicateArities.end(); ++it) {
00359             // for predicate foo of arity k create constraint
00360             // :- foo(X1,X2,...,Xk), foo_neg_aux(X1,X2,...,Xk).
00361 
00362             // create atoms
00363             const ID idpred = it->first;
00364             BOOST_FOREACH (unsigned arity, it->second) {
00365                 DBGLOG(DBG,"processing predicate '" <<
00366                     printToString<RawPrinter>(idpred, reg) << "'/" << idpred <<
00367                     " with arity " << arity);
00368 
00369                 const ID idnegpred = reg->getAuxiliaryConstantSymbol('s', idpred);
00370                 ID idatom;
00371                 ID idnegatom;
00372                 if( arity == 0 ) {
00373                     // ground atoms
00374                     OrdinaryAtom predAtom(
00375                         ID::MAINKIND_ATOM |
00376                         ID::SUBKIND_ATOM_ORDINARYG);
00377                     predAtom.tuple.push_back(idpred);
00378                     OrdinaryAtom negpredAtom(
00379                         ID::MAINKIND_ATOM |
00380                         ID::SUBKIND_ATOM_ORDINARYG |
00381                         ID::PROPERTY_AUX);
00382                     negpredAtom.tuple.push_back(idnegpred);
00383                     idatom = reg->storeOrdinaryGAtom(predAtom);
00384                     idnegatom = reg->storeOrdinaryGAtom(negpredAtom);
00385                 }
00386                 else {
00387                     // nonground atoms
00388                     OrdinaryAtom predAtom(
00389                         ID::MAINKIND_ATOM |
00390                         ID::SUBKIND_ATOM_ORDINARYN);
00391                     predAtom.tuple.push_back(idpred);
00392                     OrdinaryAtom negpredAtom(
00393                         ID::MAINKIND_ATOM |
00394                         ID::SUBKIND_ATOM_ORDINARYN |
00395                         ID::PROPERTY_AUX);
00396                     negpredAtom.tuple.push_back(idnegpred);
00397 
00398                     // add variables
00399                     for(unsigned i = 0; i < arity; ++i) {
00400                         // create variable
00401                         std::ostringstream s;
00402                         s << "X" << i;
00403                         Term var(
00404                             ID::MAINKIND_TERM |
00405                             ID::SUBKIND_TERM_VARIABLE |
00406                             ID::PROPERTY_AUX,
00407                             s.str());
00408                         const ID idvar = reg->storeConstOrVarTerm(var);
00409                         predAtom.tuple.push_back(idvar);
00410                         negpredAtom.tuple.push_back(idvar);
00411                     }
00412 
00413                     DBGLOG(DBG,"storing auxiliary atom " << predAtom);
00414                     idatom = reg->storeOrdinaryNAtom(predAtom);
00415                     DBGLOG(DBG,"storing auxiliary negative atom " << negpredAtom);
00416                     idnegatom = reg->storeOrdinaryNAtom(negpredAtom);
00417                 }
00418 
00419                 // create constraint
00420                 Rule r(
00421                     ID::MAINKIND_RULE |
00422                     ID::SUBKIND_RULE_CONSTRAINT |
00423                     ID::PROPERTY_AUX);
00424 
00425                 r.body.push_back(ID::posLiteralFromAtom(idatom));
00426                 r.body.push_back(ID::posLiteralFromAtom(idnegatom));
00427 
00428                 ID idcon = reg->storeRule(r);
00429                 ctx.idb.push_back(idcon);
00430                 DBGLOG(DBG,"created aux constraint '" <<
00431                     printToString<RawPrinter>(idcon, reg) << "'");
00432             }
00433         }
00434     }
00435 
00436 }                                // anonymous namespace
00437 
00438 
00439 // rewrite program by adding auxiliary query rules
00440 PluginRewriterPtr StrongNegationPlugin::createRewriter(ProgramCtx& ctx)
00441 {
00442     StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>();
00443     if( !ctxdata.enabled )
00444         return PluginRewriterPtr();
00445 
00446     return PluginRewriterPtr(new StrongNegationConstraintAdder);
00447 }
00448 
00449 
00450 namespace
00451 {
00452 
00453     class NegAuxPrinter:
00454     public AuxPrinter
00455     {
00456         public:
00457             typedef StrongNegationPlugin::CtxData::NegToPosMap NegToPosMap;
00458         public:
00459             NegAuxPrinter(
00460                 RegistryPtr reg,
00461                 PredicateMask& negAuxMask,
00462                 const NegToPosMap& ntpm):
00463             reg(reg), mask(negAuxMask), ntpm(ntpm) {
00464             }
00465 
00466             // print an ID and return true,
00467             // or do not print it and return false
00468             virtual bool print(std::ostream& out, ID id, const std::string& prefix) const
00469             {
00470                 assert(id.isAuxiliary());
00471                 mask.updateMask();
00472                 DBGLOG(DBG,"mask is " << *mask.mask());
00473                 if( mask.mask()->getFact(id.address) ) {
00474                     // we cannot use any stored text to print this, we have to assemble it from pieces
00475                     DBGLOG(DBG,"printing auxiliary for strong negation: " << id);
00476 
00477                     // get replacement atom details
00478                     const OrdinaryAtom& r_atom = reg->ogatoms.getByAddress(id.address);
00479 
00480                     // find positive version of predicate
00481                     assert(!r_atom.tuple.empty());
00482                     const NegToPosMap::const_iterator itpred = ntpm.find(r_atom.tuple.front());
00483                     assert(itpred != ntpm.end());
00484                     const ID idpred = itpred->second;
00485 
00486                     // print strong negation
00487                     out << prefix << '-';
00488 
00489                     // print tuple
00490                     RawPrinter printer(out, reg);
00491                     // predicate
00492                     printer.print(idpred);
00493                     if( r_atom.tuple.size() > 1 ) {
00494                         Tuple t(r_atom.tuple.begin()+1, r_atom.tuple.end());
00495                         out << "(";
00496                         printer.printmany(t,",");
00497                         out << ")";
00498                     }
00499 
00500                     return true;
00501                 }
00502                 return false;
00503             }
00504 
00505         protected:
00506             RegistryPtr reg;
00507             PredicateMask& mask;
00508             const NegToPosMap& ntpm;
00509     };
00510 
00511 }                                // anonymous namespace
00512 
00513 
00514 // register auxiliary printer for strong negation auxiliaries
00515 void StrongNegationPlugin::setupProgramCtx(ProgramCtx& ctx)
00516 {
00517     StrongNegationPlugin::CtxData& ctxdata = ctx.getPluginData<StrongNegationPlugin>();
00518     if( !ctxdata.enabled )
00519         return;
00520 
00521     RegistryPtr reg = ctx.registry();
00522 
00523     // init predicate mask
00524     ctxdata.myAuxiliaryPredicateMask.setRegistry(reg);
00525 
00526     // add all auxiliaries to mask (here we should already have parsed all of them)
00527     typedef CtxData::NegToPosMap NegToPosMap;
00528     NegToPosMap::const_iterator it;
00529     for(it = ctxdata.negToPos.begin();
00530     it != ctxdata.negToPos.end(); ++it) {
00531         ctxdata.myAuxiliaryPredicateMask.addPredicate(it->first);
00532     }
00533 
00534     // update predicate mask
00535     ctxdata.myAuxiliaryPredicateMask.updateMask();
00536 
00537     // create auxiliary printer using mask
00538     AuxPrinterPtr negAuxPrinter(new NegAuxPrinter(
00539         reg, ctxdata.myAuxiliaryPredicateMask, ctxdata.negToPos));
00540     reg->registerUserAuxPrinter(negAuxPrinter);
00541 }
00542 
00543 
00544 DLVHEX_NAMESPACE_END
00545 
00546 // this would be the code to use this plugin as a "real" plugin in a .so file
00547 // but we directly use it in dlvhex.cpp
00548 #if 0
00549 StrongNegationPlugin theStrongNegationPlugin;
00550 
00551 // return plain C type s.t. all compilers and linkers will like this code
00552 extern "C"
00553 void * PLUGINIMPORTFUNCTION()
00554 {
00555     return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theStrongNegationPlugin);
00556 }
00557 #endif
00558 
00559 
00560 // vim:expandtab:ts=4:sw=4:
00561 // mode: C++
00562 // End: