dlvhex  2.5.0
src/ChoicePlugin.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/ChoicePlugin.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 #include "dlvhex2/LiberalSafetyChecker.h"
00052 
00053 #include <boost/algorithm/string/predicate.hpp>
00054 #include <boost/lexical_cast.hpp>
00055 
00056 DLVHEX_NAMESPACE_BEGIN
00057 
00058 namespace spirit = boost::spirit;
00059 namespace qi = boost::spirit::qi;
00060 
00061 ChoicePlugin::CtxData::CtxData():
00062 enabled(false)
00063 {
00064 }
00065 
00066 
00067 ChoicePlugin::ChoicePlugin():
00068 PluginInterface()
00069 {
00070     setNameVersion("dlvhex-choicePlugin[internal]", 2, 0, 0);
00071 }
00072 
00073 
00074 ChoicePlugin::~ChoicePlugin()
00075 {
00076 }
00077 
00078 
00079 // output help message for this plugin
00080 void ChoicePlugin::printUsage(std::ostream& o) const
00081 {
00082     //    123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-
00083     o << "     --choice-enable[=true,false]" << std::endl
00084         << "                      Enable choice rules (default is enabled)." << std::endl;
00085 }
00086 
00087 
00088 // accepted options: --choice-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 ChoicePlugin::processOptions(
00093 std::list<const char*>& pluginOptions,
00094 ProgramCtx& ctx)
00095 {
00096     ChoicePlugin::CtxData& ctxdata = ctx.getPluginData<ChoicePlugin>();
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, "--choice-enable" ) ) {
00107             std::string m = str.substr(std::string("--choice-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 --choice-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,"ChoicePlugin successfully processed option " << str);
00125             it = pluginOptions.erase(it);
00126         }
00127         else {
00128             it++;
00129         }
00130     }
00131 }
00132 
00133 
00134 class ChoiceParserModuleSemantics:
00135 public HexGrammarSemantics
00136 {
00137     public:
00138         ChoicePlugin::CtxData& ctxdata;
00139 
00140     public:
00141         ChoiceParserModuleSemantics(ProgramCtx& ctx):
00142         HexGrammarSemantics(ctx),
00143         ctxdata(ctx.getPluginData<ChoicePlugin>()) {
00144         }
00145 
00146         // use SemanticActionBase to redirect semantic action call into globally
00147         // specializable sem<T> struct space
00148         struct choiceRule:
00149         SemanticActionBase<ChoiceParserModuleSemantics, ID, choiceRule>
00150         {
00151             choiceRule(ChoiceParserModuleSemantics& mgr):
00152             choiceRule::base_type(mgr) {
00153             }
00154         };
00155         struct choiceHead:
00156         SemanticActionBase<ChoiceParserModuleSemantics, Tuple, choiceHead>
00157         {
00158             choiceHead(ChoiceParserModuleSemantics& mgr):
00159             choiceHead::base_type(mgr) {
00160             }
00161         };
00162         struct choiceElement:
00163         SemanticActionBase<ChoiceParserModuleSemantics, ID, choiceElement>
00164         {
00165             choiceElement(ChoiceParserModuleSemantics& mgr):
00166             choiceElement::base_type(mgr) {
00167             }
00168         };
00169 };
00170 
00171 // create semantic handler for above semantic action
00172 // (needs to be in globally specializable struct space)
00173 template<>
00174 struct sem<ChoiceParserModuleSemantics::choiceRule>
00175 {
00176     void operator()(
00177         ChoiceParserModuleSemantics& mgr,
00178         const boost::fusion::vector2<
00179         dlvhex::Tuple,
00180         boost::optional<std::vector<dlvhex::ID> >
00181         >& source,
00182     ID& target) {
00183         RegistryPtr reg = mgr.ctx.registry();
00184 
00185         // add original rule body to all rewritten rules
00186         Tuple rules = boost::fusion::at_c<0>(source);
00187         if (!!boost::fusion::at_c<1>(source)) {
00188             int i = 0;
00189             BOOST_FOREACH (ID ruleID, rules) {
00190                 Rule rule = reg->rules.getByID(ruleID);
00191                 rule.body.insert(rule.body.end(), boost::fusion::at_c<1>(source).get().begin(), boost::fusion::at_c<1>(source).get().end());
00192                 // check if the rule contains external atoms
00193                 BOOST_FOREACH (ID b, rule.body) {
00194                     if (b.isExternalAtom()) {
00195                         rule.kind |= ID::PROPERTY_RULE_EXTATOMS;
00196                         break;
00197                     }
00198                 }
00199                 rules[i] = reg->storeRule(rule);
00200                 i++;
00201             }
00202         }
00203         BOOST_FOREACH (ID ruleID, rules) {
00204             if ( mgr.mlpMode == 0 ) {
00205                 mgr.ctx.idb.push_back(ruleID);
00206             }
00207             else {
00208                 mgr.ctx.idbList.back().push_back(ruleID);
00209             }
00210             // return ID of last rule
00211             target = ID_FAIL;
00212         }
00213     }
00214 };
00215 template<>
00216 struct sem<ChoiceParserModuleSemantics::choiceHead>
00217 {
00218     void operator()(
00219         ChoiceParserModuleSemantics& mgr,
00220         const boost::fusion::vector3<
00221         boost::optional<boost::fusion::vector2<ID, ID> >,
00222         boost::optional<std::vector<dlvhex::ID> >,
00223         boost::optional<boost::fusion::vector2<ID, ID> >
00224         >& source,
00225     Tuple& target) {
00226         RegistryPtr reg = mgr.ctx.registry();
00227 
00228         // constraint choice according to bounds
00229         Rule r(ID::MAINKIND_RULE | ID::SUBKIND_RULE_CONSTRAINT);
00230 
00231         int varnr = 1;
00232 
00233         // Step 1: Create builtins of kind: "not l <= V1" and "not r <= u"
00234         //         (where l and u are the lower and upper bounds and <= can also be different comparison operators)
00235         BuiltinAtom bound1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_BUILTIN);
00236         BuiltinAtom bound2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_BUILTIN);
00237         {
00238             // left bound
00239             if (!!boost::fusion::at_c<0>(source)) {
00240                 IDAddress adr = boost::fusion::at_c<1>(boost::fusion::at_c<0>(source).get()).address;
00241                 bound1.tuple.push_back(ID::termFromBuiltin(static_cast<dlvhex::ID::TermBuiltinAddress>(ID::negateBinaryOperator(adr))));
00242                 bound1.tuple.push_back(boost::fusion::at_c<0>(boost::fusion::at_c<0>(source).get()));
00243                 bound1.tuple.push_back(reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr)));
00244             }
00245 
00246             // left right bound
00247             if (!!boost::fusion::at_c<2>(source)) {
00248                 IDAddress adr = boost::fusion::at_c<0>(boost::fusion::at_c<2>(source).get()).address;
00249                 bound2.tuple.push_back(ID::termFromBuiltin(static_cast<dlvhex::ID::TermBuiltinAddress>(ID::negateBinaryOperator(adr))));
00250                 bound2.tuple.push_back(reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr)));
00251                 bound2.tuple.push_back(boost::fusion::at_c<1>(boost::fusion::at_c<2>(source).get()));
00252             }
00253 
00254             // default
00255             if (!boost::fusion::at_c<0>(source) && !boost::fusion::at_c<1>(source)) {
00256                 bound1.tuple.push_back(ID::termFromBuiltin(ID::TERM_BUILTIN_GE));
00257                 bound1.tuple.push_back(reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr)));
00258                 bound1.tuple.push_back(ID::termFromInteger(0));
00259             }
00260         }
00261 
00262         // Step 2: compute V1 as the sum of all counts of choice elements
00263         ID cntVarID = reg->getAuxiliaryVariableSymbol('c', ID::termFromInteger(varnr));
00264         DBGLOG(DBG, "Creating aggregate cntVarID=#count{ ChoiceAtom(...) : ChoiceCondition(...) }");
00265         AggregateAtom cnt(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_AGGREGATE);
00266         cnt.tuple[0] = cntVarID;
00267         cnt.tuple[1] = ID::termFromBuiltin(ID::TERM_BUILTIN_EQ);
00268         cnt.tuple[2] = ID::termFromBuiltin(ID::TERM_BUILTIN_AGGCOUNT);
00269         cnt.tuple[3] = ID_FAIL;
00270         cnt.tuple[4] = ID_FAIL;
00271         varnr += 1;              // one variable was used
00272 
00273         // create count atom
00274         if (!!boost::fusion::at_c<1>(source)) {
00275             int elementNr = 0;
00276             BOOST_FOREACH (ID choiceElement, boost::fusion::at_c<1>(source).get()) {
00277                 // copy choice rule
00278                 target.push_back(choiceElement);
00279 
00280                 // extract choice atom as first element of the rule's head
00281                 ID choiceAtomID = reg->rules.getByID(choiceElement).head[0];
00282 
00283                 // constructor an aggregate of kind: V[i+1]=#count{ ChoiceAtom(...) : ChoiceCondition(...) }
00284                 ID cntID;
00285                 std::set<ID> vars;
00286                 reg->getVariablesInID(choiceAtomID, vars);
00287                 Tuple curVarVector;
00288                 curVarVector.insert(curVarVector.end(), vars.begin(), vars.end());
00289                 curVarVector.push_back(ID::termFromInteger(elementNr++));
00290                 cnt.mvariables.push_back(curVarVector);
00291                 Tuple curLitVector;
00292                 curLitVector.push_back(ID::posLiteralFromAtom(choiceAtomID));
00293                 cnt.mliterals.push_back(curLitVector);
00294             }
00295         }
00296         ID cntID = reg->aatoms.storeAndGetID(cnt);
00297         DBGLOG(DBG, "Result: " << printToString<RawPrinter>(cntID, reg));
00298         r.body.push_back(ID::posLiteralFromAtom(cntID));
00299 
00300         // Add up to two choice constraints. Note: The rule body of the original choice rule is still missing!
00301         if (bound1.tuple.size() > 0) {
00302             DBGLOG(DBG, "Checking bound 1");
00303             ID boundID = reg->batoms.storeAndGetID(bound1);
00304             DBGLOG(DBG, "Bound atom 1: " << printToString<RawPrinter>(boundID, reg));
00305             r.body.push_back(ID::posLiteralFromAtom(boundID));
00306             ID consRuleID = reg->storeRule(r);
00307             DBGLOG(DBG, "Choice constraint 1: " << printToString<RawPrinter>(consRuleID, reg));
00308             target.push_back(consRuleID);
00309             r.body.pop_back();
00310         }
00311         if (bound2.tuple.size() > 0) {
00312             DBGLOG(DBG, "Checking bound 2");
00313             ID boundID = reg->batoms.storeAndGetID(bound2);
00314             DBGLOG(DBG, "Bound atom 2: " << printToString<RawPrinter>(boundID, reg));
00315             r.body.push_back(ID::posLiteralFromAtom(boundID));
00316             ID consRuleID = reg->storeRule(r);
00317             DBGLOG(DBG, "Choice constraint 2: " << printToString<RawPrinter>(consRuleID, reg));
00318             target.push_back(consRuleID);
00319             r.body.pop_back();
00320         }
00321     }
00322 };
00323 template<>
00324 struct sem<ChoiceParserModuleSemantics::choiceElement>
00325 {
00326     void operator()(
00327         ChoiceParserModuleSemantics& mgr,
00328         const boost::fusion::vector2<
00329         dlvhex::ID,
00330         boost::optional<boost::optional<std::vector<dlvhex::ID> > >
00331         >& source,
00332     ID& target) {
00333         RegistryPtr reg = mgr.ctx.registry();
00334 
00335         // guess between choice atom and negated choice atom
00336         Rule r(ID::MAINKIND_RULE | ID::PROPERTY_RULE_DISJ);
00337         ID choiceAtomID = boost::fusion::at_c<0>(source);
00338         r.head.push_back(choiceAtomID);
00339         OrdinaryAtom negChoiceAtom = reg->lookupOrdinaryAtom(choiceAtomID);
00340         negChoiceAtom.tuple[0] = reg->getAuxiliaryConstantSymbol('c', negChoiceAtom.tuple[0]);
00341         negChoiceAtom.kind |= ID::PROPERTY_AUX;
00342         ID negChoiceAtomID = reg->storeOrdinaryAtom(negChoiceAtom);
00343         r.head.push_back(negChoiceAtomID);
00344 
00345         // add condition of choice element to rule body if available
00346         if ((!!boost::fusion::at_c<1>(source)) && (!!boost::fusion::at_c<1>(source).get())) {
00347             r.body = boost::fusion::at_c<1>(source).get().get();
00348         }
00349 
00350         // Note: The rule body of the original choice rule is still missing!
00351         target = reg->storeRule(r);
00352     }
00353 };
00354 
00355 namespace
00356 {
00357 
00358     template<typename Iterator, typename Skipper>
00359         struct ChoiceParserModuleGrammarBase:
00360     // we derive from the original hex grammar
00361     // -> we can reuse its rules
00362     public HexGrammarBase<Iterator, Skipper>
00363     {
00364         typedef HexGrammarBase<Iterator, Skipper> Base;
00365 
00366         ChoiceParserModuleSemantics& sem;
00367 
00368         ChoiceParserModuleGrammarBase(ChoiceParserModuleSemantics& sem):
00369         Base(sem),
00370         sem(sem) {
00371             typedef ChoiceParserModuleSemantics Sem;
00372 
00373             choiceRule
00374                 = (choiceHead >> -(qi::lit(":-") > (Base::bodyLiteral % qi::char_(',')) ) >> qi::lit('.') ) [ Sem::choiceRule(sem) ];
00375 
00376             choiceHead
00377                 = (
00378                 -(Base::term >> Base::builtinOpsBinary) >> qi::lit('{') >> -(choiceElement % qi::lit(';')) >> qi::lit('}') >> -(Base::builtinOpsBinary >> Base::term) > qi::eps
00379                 ) [ Sem::choiceHead(sem) ];
00380 
00381             choiceElement
00382                 = (
00383                 Base::classicalAtom >> -(qi::lit(':') >> (Base::bodyLiteral % qi::lit(','))) > qi::eps
00384                 ) [ Sem::choiceElement(sem) ];
00385 
00386             #ifdef BOOST_SPIRIT_DEBUG
00387             BOOST_SPIRIT_DEBUG_NODE(choiceHead);
00388             BOOST_SPIRIT_DEBUG_NODE(choiceElement);
00389             #endif
00390         }
00391 
00392         qi::rule<Iterator, ID(), Skipper> choiceRule;
00393         qi::rule<Iterator, Tuple(), Skipper> choiceHead;
00394         qi::rule<Iterator, ID(), Skipper> choiceElement;
00395     };
00396 
00397     struct ChoiceParserModuleGrammar:
00398     ChoiceParserModuleGrammarBase<HexParserIterator, HexParserSkipper>,
00399     // required for interface
00400     // note: HexParserModuleGrammar =
00401     //       boost::spirit::qi::grammar<HexParserIterator, HexParserSkipper>
00402         HexParserModuleGrammar
00403     {
00404         typedef ChoiceParserModuleGrammarBase<HexParserIterator, HexParserSkipper> GrammarBase;
00405         typedef HexParserModuleGrammar QiBase;
00406 
00407         ChoiceParserModuleGrammar(ChoiceParserModuleSemantics& sem):
00408         GrammarBase(sem),
00409         QiBase(GrammarBase::choiceRule) {
00410         }
00411     };
00412     typedef boost::shared_ptr<ChoiceParserModuleGrammar>
00413         ChoiceParserModuleGrammarPtr;
00414 
00415     // moduletype = HexParserModule::TOPLEVEL
00416     template<enum HexParserModule::Type moduletype>
00417     class ChoiceParserModule:
00418     public HexParserModule
00419     {
00420         public:
00421             // the semantics manager is stored/owned by this module!
00422             ChoiceParserModuleSemantics sem;
00423             // we also keep a shared ptr to the grammar module here
00424             ChoiceParserModuleGrammarPtr grammarModule;
00425 
00426             ChoiceParserModule(ProgramCtx& ctx):
00427             HexParserModule(moduletype),
00428             sem(ctx) {
00429                 LOG(INFO,"constructed ChoiceParserModule");
00430             }
00431 
00432             virtual HexParserModuleGrammarPtr createGrammarModule() {
00433                 assert(!grammarModule && "for simplicity (storing only one grammarModule pointer) we currently assume this will be called only once .. should be no problem to extend");
00434                 grammarModule.reset(new ChoiceParserModuleGrammar(sem));
00435                 LOG(INFO,"created ChoiceParserModuleGrammar");
00436                 return grammarModule;
00437             }
00438     };
00439 
00440 }                                // anonymous namespace
00441 
00442 
00443 // create parser modules that extend and the basic hex grammar
00444 // this parser also stores the query information into the plugin
00445 std::vector<HexParserModulePtr>
00446 ChoicePlugin::createParserModules(ProgramCtx& ctx)
00447 {
00448     DBGLOG(DBG,"ChoicePlugin::createParserModules()");
00449     std::vector<HexParserModulePtr> ret;
00450 
00451     ChoicePlugin::CtxData& ctxdata = ctx.getPluginData<ChoicePlugin>();
00452     if( ctxdata.enabled ) {
00453         ret.push_back(HexParserModulePtr(
00454             new ChoiceParserModule<HexParserModule::TOPLEVEL>(ctx)));
00455     }
00456 
00457     return ret;
00458 }
00459 
00460 
00461 std::vector<PluginAtomPtr> ChoicePlugin::createAtoms(ProgramCtx& ctx) const
00462 {
00463     std::vector<PluginAtomPtr> ret;
00464     // we don't have external atoms, only a parer plugin and a rewriter
00465     return ret;
00466 }
00467 
00468 
00469 void ChoicePlugin::setupProgramCtx(ProgramCtx& ctx)
00470 {
00471     ChoicePlugin::CtxData& ctxdata = ctx.getPluginData<ChoicePlugin>();
00472     if( !ctxdata.enabled )
00473         return;
00474 
00475     RegistryPtr reg = ctx.registry();
00476 }
00477 
00478 
00479 DLVHEX_NAMESPACE_END
00480 
00481 // this would be the code to use this plugin as a "real" plugin in a .so file
00482 // but we directly use it in dlvhex.cpp
00483 #if 0
00484 ChoicePlugin theChoicePlugin;
00485 
00486 // return plain C type s.t. all compilers and linkers will like this code
00487 extern "C"
00488 void * PLUGINIMPORTFUNCTION()
00489 {
00490     return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theChoicePlugin);
00491 }
00492 #endif
00493 
00494 
00495 // vim:expandtab:ts=4:sw=4:
00496 // mode: C++
00497 // End: