dlvhex  2.5.0
src/DependencyGraph.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 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif                           // HAVE_CONFIG_H
00039 
00040 #include "dlvhex2/DependencyGraph.h"
00041 #include "dlvhex2/LiberalSafetyChecker.h"
00042 #include "dlvhex2/Logger.h"
00043 #include "dlvhex2/Registry.h"
00044 #include "dlvhex2/ProgramCtx.h"
00045 #include "dlvhex2/Printer.h"
00046 #include "dlvhex2/Rule.h"
00047 #include "dlvhex2/Atoms.h"
00048 #include "dlvhex2/PluginInterface.h"
00049 #include "dlvhex2/GraphvizHelpers.h"
00050 
00051 #include <boost/property_map/property_map.hpp>
00052 #include <boost/foreach.hpp>
00053 #include <boost/algorithm/string/replace.hpp>
00054 #include <boost/range/join.hpp>
00055 
00056 #include <sstream>
00057 
00058 DLVHEX_NAMESPACE_BEGIN
00059 
00060 const DependencyGraph::DependencyInfo&
00061 DependencyGraph::DependencyInfo::operator|=(
00062 const DependencyGraph::DependencyInfo& other)
00063 {
00064     positiveRegularRule |= other.positiveRegularRule;
00065     positiveConstraint |= other.positiveConstraint;
00066     negativeRule |= other.negativeRule;
00067     unifyingHead |= other.unifyingHead;
00068     disjunctive |= other.disjunctive;
00069     positiveExternal |= other.positiveExternal;
00070     negativeExternal |= other.negativeExternal;
00071     externalConstantInput |= other.externalConstantInput;
00072     externalPredicateInput |= other.externalPredicateInput;
00073     externalNonmonotonicPredicateInput |= other.externalNonmonotonicPredicateInput;
00074     return *this;
00075 }
00076 
00077 
00078 std::ostream& DependencyGraph::NodeInfo::print(std::ostream& o) const
00079 {
00080     return o << "id=" << id;
00081 }
00082 
00083 
00084 std::ostream& DependencyGraph::DependencyInfo::print(std::ostream& o) const
00085 {
00086     return o <<
00087         (positiveRegularRule?" positiveRegularRule":"") <<
00088         (positiveConstraint?" positiveConstraint":"") <<
00089         (negativeRule?" negativeRule":"") <<
00090         (unifyingHead?" unifyingHead":"") <<
00091         (disjunctive?" disjunctive":"") <<
00092         (positiveExternal?" positiveExternal":"") <<
00093         (negativeExternal?" negativeExternal":"") <<
00094         (externalConstantInput?" externalConstantInput":"") <<
00095         (externalPredicateInput?" externalPredicateInput":"") <<
00096         (externalNonmonotonicPredicateInput?" externalNonmonotonicPredicateInput":"");
00097 }
00098 
00099 
00100 DependencyGraph::DependencyGraph(ProgramCtx& ctx, RegistryPtr registry):
00101 ctx(ctx), registry(registry), dg(), nm()
00102 {
00103 }
00104 
00105 
00106 DependencyGraph::~DependencyGraph()
00107 {
00108 }
00109 
00110 
00111 void DependencyGraph::createDependencies(
00112 const std::vector<ID>& idb,
00113 std::vector<ID>& createdAuxRules)
00114 {
00115     HeadBodyHelper hbh;
00116     createNodesAndIntraRuleDependencies(idb, createdAuxRules, hbh);
00117     createExternalPredicateInputDependencies(hbh);
00118     createUnifyingDependencies(hbh);
00119     // aggregate dependencies are put into rule dependencies
00120     // (they do not generate separate nodes)
00121 }
00122 
00123 
00124 // create nodes for rules and external atoms
00125 // create "positiveExternal" and "negativeExternal" dependencies
00126 // create "externalConstantInput" dependencies and auxiliary rules
00127 // fill HeadBodyHelper (required for efficient unification)
00128 void DependencyGraph::createNodesAndIntraRuleDependencies(
00129 const std::vector<ID>& idb,
00130 std::vector<ID>& createdAuxRules, HeadBodyHelper& hbh)
00131 {
00132     // TODO: faster allocation of dep graph? use custom storage with pre-reserved memory? (we know the exact number of nodes from the registry!)
00133     DBGLOG_SCOPE(ANALYZE,"cNaIRD", false);
00134     DBGLOG(DBG,"=createNodesAndIntraRuleDependencies");
00135 
00136     // create nodes and register them in node mapping table
00137     BOOST_FOREACH(ID idrule, idb) {
00138         createNodesAndIntraRuleDependenciesForRule(idrule, createdAuxRules, hbh);
00139     }                            // FOREACH id in idb
00140 }
00141 
00142 
00143 void DependencyGraph::createNodesAndIntraRuleDependenciesForRuleAddHead(
00144 ID idat, const Rule& rule, Node nrule, HeadBodyHelper& hbh)
00145 {
00146     const HeadBodyHelper::IDIndex& hbh_ididx = hbh.infos.get<IDTag>();
00147 
00148     DBGLOG(DBG,"adding head item " << idat);
00149     assert(idat.isAtom());
00150     assert(idat.isOrdinaryAtom());
00151 
00152     HeadBodyHelper::IDIndex::const_iterator it =
00153         hbh_ididx.find(idat);
00154     if( it == hbh_ididx.end() ) {
00155         // new one -> insert
00156         HeadBodyInfo hbi(&(registry->lookupOrdinaryAtom(idat)));
00157         hbi.id = idat;
00158         hbi.inHead = true;
00159         if( rule.head.size() > 1 ) {
00160             hbi.inHeadOfDisjunctiveRules.push_back(nrule);
00161         }
00162         else {
00163             hbi.inHeadOfNondisjunctiveRules.push_back(nrule);
00164         }
00165         if( hbi.oatom->tuple[0].isConstantTerm() )
00166             hbi.headPredicate = hbi.oatom->tuple[0];
00167         hbh.infos.insert(hbi);
00168     }
00169     else {
00170         // existing one -> update
00171         HeadBodyInfo hbi(*it);
00172         assert(hbi.id == idat);
00173         if( hbi.inHead == false ) {
00174             if( hbi.oatom->tuple[0].isConstantTerm() )
00175                 hbi.headPredicate = hbi.oatom->tuple[0];
00176         }
00177         hbi.inHead = true;
00178         if( rule.head.size() > 1 ) {
00179             hbi.inHeadOfDisjunctiveRules.push_back(nrule);
00180         }
00181         else {
00182             hbi.inHeadOfNondisjunctiveRules.push_back(nrule);
00183         }
00184         bool success = hbh.infos.replace(it, hbi);
00185         assert(success);
00186     }
00187 }
00188 
00189 
00190 void DependencyGraph::createNodesAndIntraRuleDependenciesForBody(
00191 ID idlit, ID idrule, const Tuple& body, Node nrule,
00192 HeadBodyHelper& hbh, std::vector<ID>& createdAuxRules,
00193 bool inAggregateBody)
00194 {
00195     // inAggregateBody is true if the currently processed literal idlit
00196     // occurs in the body of some aggregate atom:
00197     // in this case we need to add positive and negative dependencies
00198     const HeadBodyHelper::IDIndex& hbh_ididx = hbh.infos.get<IDTag>();
00199 
00200     DBGLOG(DBG,"adding body literal " << idlit);
00201     assert(idlit.isLiteral());
00202     bool naf = idlit.isNaf();
00203     ID idat = ID::atomFromLiteral(idlit);
00204 
00205     if( idat.isOrdinaryAtom() ) {
00206         // lookup as atom
00207         HeadBodyHelper::IDIndex::const_iterator it =
00208             hbh_ididx.find(idat);
00209         if( it == hbh_ididx.end() ) {
00210             // new one -> insert
00211             HeadBodyInfo hbi(&(registry->lookupOrdinaryAtom(idat)));
00212             hbi.id = idat;
00213             hbi.inBody = true;
00214             if( naf || inAggregateBody ) {
00215                 hbi.inNegBodyOfRules.push_back(nrule);
00216             }
00217             if ( !naf || inAggregateBody ) {
00218                 if( idrule.isRegularRule() )
00219                     hbi.inPosBodyOfRegularRules.push_back(nrule);
00220                 else
00221                     hbi.inPosBodyOfConstraints.push_back(nrule);
00222             }
00223             hbh.infos.insert(hbi);
00224         }
00225         else {
00226             // existing one -> update
00227             HeadBodyInfo hbi(*it);
00228             assert(hbi.id == idat);
00229             hbi.inBody = true;
00230             if( naf || inAggregateBody ) {
00231                 hbi.inNegBodyOfRules.push_back(nrule);
00232             }
00233             if ( !naf || inAggregateBody ) {
00234                 if( idrule.isRegularRule() )
00235                     hbi.inPosBodyOfRegularRules.push_back(nrule);
00236                 else
00237                     hbi.inPosBodyOfConstraints.push_back(nrule);
00238             }
00239             bool success = hbh.infos.replace(it, hbi);
00240             assert(success);
00241         }
00242     }                            // treat ordinary body atoms
00243     else if( idat.isExternalAtom() ) {
00244         // retrieve eatom from registry
00245         const ExternalAtom& eatom = registry->eatoms.getByID(idat);
00246 
00247         assert(!!eatom.pluginAtom);
00248         PluginAtom* pluginAtom = eatom.pluginAtom;
00249 
00250         // make sure the meta information fits the external atom
00251         // (only assert here, should be ensured by plugin loading or parsing)
00252         assert(pluginAtom->checkInputArity(eatom.inputs.size()));
00253         assert(pluginAtom->checkOutputArity(eatom.getExtSourceProperties(), eatom.tuple.size()));
00254 
00255         // create new node only if not already present
00256         // (see testcase extatom2.hex)
00257         const NodeIDIndex& idx = nm.get<IDTag>();
00258         NodeIDIndex::const_iterator it = idx.find(idat);
00259         Node neatom;
00260         if( it == idx.end() ) {
00261             DBGLOG(DBG,"adding external atom " << eatom << " with id " << idat);
00262 
00263             // new node for eatom
00264             neatom = createNode(idat);
00265 
00266             // create auxiliary rule for this eatom in this rule
00267             createAuxiliaryRuleIfRequired(
00268                 body,
00269                 idlit, idat, neatom, eatom, pluginAtom,
00270                 createdAuxRules,
00271                 hbh);
00272         }
00273         else {
00274             DBGLOG(DBG,"reusing external atom " << eatom << " with id " << idat);
00275             neatom = it->node;
00276         }
00277 
00278         // add dependency from rule to external atom depending on monotonicity
00279         // (positiveExternal vs negativeExternal vs both)
00280 
00281         //    bool monotonic = pluginAtom->isMonotonic();
00282         bool monotonic = eatom.getExtSourceProperties().isMonotonic();
00283 
00284         // store dependency
00285         DBGLOG(DBG,"storing dependency: " << idrule << " -> " << idat <<
00286             " with monotonic=" << monotonic << ", naf=" << naf);
00287 
00288         DependencyInfo diExternal;
00289         // positive dependency whenever positive or nonmonotonic or in an aggregate atom
00290         diExternal.positiveExternal = (!monotonic || !naf || inAggregateBody);
00291         // negative dependency whenever negative or nonmonotonic or in an aggregate atom
00292         diExternal.negativeExternal = (!monotonic || naf || inAggregateBody);
00293 
00294         Dependency dep;
00295         bool success;
00296         boost::tie(dep, success) = boost::add_edge(nrule, neatom, diExternal, dg);
00297         assert(success);
00298     }                            // treat external body atoms
00299     else if( idat.isBuiltinAtom() ) {
00300         // do not need to do anything about builtins
00301     }
00302     else if( idat.isAggregateAtom() ) {
00303         // retrieve aatom from registry
00304         const AggregateAtom& aatom = registry->aatoms.getByID(idat);
00305 
00306         DBGLOG_SCOPE(DBG, "recursive cNAIRDfRAB", false);
00307         DBGLOG(DBG, "=recursively calling createNodesAndIntraRuleDependenciesForRuleAddBody for aggregate " << aatom);
00308 
00309         // do the same for aggregate body as we did for the rule body
00310         // (including generation of auxiliary input rules)
00311         // (this can become recursive)
00312         BOOST_FOREACH(ID idlit_recursive, aatom.literals) {
00313             // @TODO Consider passing true as the last argument to add negative dependencies to atoms in aggregates
00314             createNodesAndIntraRuleDependenciesForBody(
00315                 idlit_recursive, idrule, aatom.literals, nrule, hbh, createdAuxRules /*, true*/);
00316         }
00317     }                            // treat aggregate body atoms
00318     else {
00319         throw FatalError("encountered unknown body atom type");
00320     }
00321 }
00322 
00323 
00324 void DependencyGraph::createNodesAndIntraRuleDependenciesForRule(
00325 ID idrule, std::vector<ID>& createdAuxRules, HeadBodyHelper& hbh)
00326 {
00327     DBGLOG_VSCOPE(DBG,"cNaIRDfR", idrule.address,true);
00328     DBGLOG(DBG,"=createNodesAndIntraRuleDependenciesForRule for rule " << idrule <<
00329         " " << printToString<RawPrinter>(idrule, registry));
00330     assert(idrule.isRule());
00331 
00332     // create new node for rule
00333     Node nrule = createNode(idrule);
00334 
00335     const Rule& rule = registry->rules.getByID(idrule);
00336 
00337     // add head atoms to hbh
00338     BOOST_FOREACH(ID idat, rule.head) {
00339         createNodesAndIntraRuleDependenciesForRuleAddHead(
00340             idat, rule, nrule, hbh);
00341     }
00342 
00343     // add body atoms to hbh
00344     BOOST_FOREACH(ID idlit, rule.body) {
00345         createNodesAndIntraRuleDependenciesForBody(
00346             idlit, idrule, rule.body, nrule, hbh, createdAuxRules);
00347     }
00348 }
00349 
00350 
00357 void DependencyGraph::createAuxiliaryRuleIfRequired(
00358 const Tuple& body,
00359 ID idlit, ID idat, Node neatom, const ExternalAtom& eatom,
00360 const PluginAtom* pluginAtom,
00361 std::vector<ID>& createdAuxRules,
00362 HeadBodyHelper& hbh)
00363 {
00364     DBGLOG_SCOPE(DBG,"cARiR",false);
00365     DBGLOG(DBG,"=createAuxiliaryRuleIfRequired for body " <<
00366         printvector(body) << " = " <<
00367         printManyToString<RawPrinter>(body, ",", registry));
00368     assert(!!pluginAtom);
00369 
00370     // collect variables at constant inputs of this external atom
00371     std::list<ID> inputVariables;
00372     std::set<ID> inputVariableSet;
00373     std::set<ID> unfoldedInputVariables;
00374 
00375     // find variables for constant inputs
00376     for(unsigned at = 0; at != eatom.inputs.size(); ++at) {
00377         if( ((pluginAtom->getInputType(at) == PluginAtom::CONSTANT) ||
00378             (pluginAtom->getInputType(at) == PluginAtom::TUPLE)
00379             ) &&
00380         (registry->getVariablesInID(eatom.inputs[at]).size() > 0) ) {
00381             ID varID = eatom.inputs[at];
00382             LOG(DBG,"at index " << at << ": found constant input that is a variable: " << varID);
00383             inputVariables.push_back(varID);
00384             inputVariableSet.insert(varID);
00385             registry->getVariablesInID(varID, unfoldedInputVariables);
00386         }
00387     }
00388 
00389     // bailout if no variables
00390     if( inputVariables.empty() )
00391         return;
00392 
00393     // build unique ordered list of input variables, and
00394     // build mapping from input variable in aux predicate to input for eatom
00395     std::list<ID> uniqueInputVariables(inputVariableSet.begin(), inputVariableSet.end());
00396     ExternalAtom::AuxInputMapping auxInputMapping;
00397     for(std::list<ID>::const_iterator ituiv = uniqueInputVariables.begin();
00398     ituiv != uniqueInputVariables.end(); ++ituiv) {
00399         std::list<unsigned> replaceWhere;
00400         for(unsigned at = 0; at != eatom.inputs.size(); ++at) {
00401             if( eatom.inputs[at] == *ituiv )
00402                 replaceWhere.push_back(at);
00403         }
00404         LOG(DBG,"auxInputMapping: aux argument " << auxInputMapping.size() <<
00405             " replaced at positions " << printrange(replaceWhere));
00406         auxInputMapping.push_back(replaceWhere);
00407     }
00408 
00409     // collect positive body literals of this rule which provide grounding
00410     // for these variables
00411     std::list<ID> auxBody;
00412     std::set<ID> groundedInputVariableSet;
00413     for(Tuple::const_iterator itat = body.begin();
00414     itat != body.end(); ++itat) {
00415         // don't compare to self
00416         if( *itat == idlit )
00417             continue;
00418 
00419         // ground atoms cannot provide grounding information
00420         if( itat->isOrdinaryGroundAtom() )
00421             continue;
00422 
00423         // see comment at top of DependencyGraph.hpp for what could perhaps be improved here
00424         // (and why only positive literals are used)
00425         if( itat->isNaf() )
00426             continue;
00427 
00428         if( itat->isExternalAtom() ) {
00429             // skip external atoms which are not necessary for safety
00430             if ( !!ctx.liberalSafetyChecker && !ctx.liberalSafetyChecker->isExternalAtomNecessaryForDomainExpansionSafety(*itat) ) {
00431                 DBGLOG(DBG, "Do not use " << *itat << " in input auxiliary rule because it is not necessary for safety");
00432                 continue;
00433             }
00434 
00435             LOG(DBG,"checking if we depend on output list of external atom " << *itat);
00436 
00437             const ExternalAtom& eatom2 =
00438                 registry->eatoms.getByID(*itat);
00439             LOG(DBG,"checking eatom " << eatom2);
00440 
00441             bool addedThis = false;
00442             std::set<ID> vars = registry->getVariablesInTuple(eatom2.tuple);
00443             for(std::set<ID>::const_iterator itvar = vars.begin();
00444             itvar != vars.end(); ++itvar) {
00445                 if( unfoldedInputVariables.count(*itvar) ) {
00446                     LOG(ANALYZE,"will ground variable " << *itvar << " by external atom " << eatom2 << " in auxiliary rule");
00447                     if( !addedThis ) {
00448                         auxBody.push_back(*itat);
00449                         addedThis = true;
00450                     }
00451                     groundedInputVariableSet.insert(*itvar);
00452                     // continue remembering which variables we already grounded
00453                 }
00454             }
00455         }                        // other body atom is external atom
00456         else if( itat->isOrdinaryNongroundAtom() || itat->isBuiltinAtom() ) {
00457             LOG(DBG,"checking if we depend on ordinary nonground/builtin atom " << *itat);
00458 
00459             const Tuple* atomtuple = 0;
00460             if( itat->isOrdinaryNongroundAtom() ) {
00461                 const OrdinaryAtom& oatom =
00462                     registry->onatoms.getByID(*itat);
00463                 LOG(DBG,"checking oatom " << oatom);
00464                 atomtuple = &oatom.tuple;
00465             }
00466             else {
00467                 assert(itat->isBuiltinAtom());
00468                 const BuiltinAtom& batom =
00469                     registry->batoms.getByID(*itat);
00470                 LOG(DBG,"checking batom " << batom);
00471                 atomtuple = &batom.tuple;
00472             }
00473             assert(!!atomtuple);
00474 
00475             bool addedThis = false;
00476             std::set<ID> vars = registry->getVariablesInTuple(*atomtuple);
00477             for(std::set<ID>::const_iterator itvar = vars.begin();
00478             itvar != vars.end(); ++itvar) {
00479                 if( unfoldedInputVariables.count(*itvar) ) {
00480                     LOG(ANALYZE,"will ground variable " << *itvar << " by atom " << printvector(*atomtuple) << " in auxiliary rule");
00481                     if( !addedThis ) {
00482                         auxBody.push_back(*itat);
00483                         addedThis = true;
00484                     }
00485                     groundedInputVariableSet.insert(*itvar);
00486                     // continue remembering which variables we already grounded
00487                 }
00488 
00489                 // @TODO: It should be possible that we add _all_ ordinary nonground atoms, even if they are not necessary.
00490                 // The more atoms we add, the more we constraint the input to external atoms and possibly avoid unnecessary evaluations.
00491                 //if (itat->isOrdinaryNongroundAtom() && !addedThis) auxBody.push_back(*itat);
00492             }                    // iterate over other body atom's arguments
00493         }
00494         else if( itat->isAggregateAtom() ) {
00495             // we don't need to consider aggregates for grounding eatom
00496         }
00497         else {
00498             std::ostringstream oss;
00499             oss << "encountered unknown atom type '" << *itat << "' in createAuxiliaryRuleIfRequired";
00500             throw FatalError(oss.str());
00501         }
00502     }                            // iterate over body of rule to find matches
00503 
00504     // check if each input variable hit at least once by auxbody
00505     if( groundedInputVariableSet != unfoldedInputVariables ) {
00506         std::stringstream s;
00507         RawPrinter printer(s, registry);
00508         s << "cannot ground external atom input variables in body '";
00509         printer.printmany(body, ", ");
00510         s << "' because of ungrounded variables ";
00511         std::vector<ID> ungrounded;
00512         BOOST_FOREACH(ID iv, inputVariableSet) {
00513             if( groundedInputVariableSet.count(iv) == 0 )
00514                 ungrounded.push_back(iv);
00515         }
00516         printer.printmany(ungrounded, ", ");
00517         throw FatalError(s.str());
00518     }
00519 
00520     assert(!auxBody.empty());
00521 
00522     // now we create an auxiliary input predicate for this rule/eatom combination
00523     // derived by a rule with body auxBody
00524 
00525     // create/invent auxiliary predicate and rule and add to registry
00526     ID auxHeadPred = createAuxiliaryRuleHeadPredicate(idat);
00527     // auxInputMask is mutable so we may store it back this way (no index on it)
00528     eatom.auxInputMask->addPredicate(auxHeadPred);
00529     ID auxHead = createAuxiliaryRuleHead(auxHeadPred, uniqueInputVariables);
00530     ID auxRule = createAuxiliaryRule(auxHead, auxBody);
00531     if( Logger::Instance().shallPrint(Logger::DBG) ) {
00532         std::stringstream s;
00533         RawPrinter printer(s, registry);
00534         s << "created auxiliary rule '";
00535         printer.print(auxRule);
00536         s << "' to ground variables '";
00537         printer.printmany(
00538             std::vector<ID>(
00539             inputVariableSet.begin(), inputVariableSet.end()),
00540             ", ");
00541         s << "' of eatom '";
00542         printer.print(idat);
00543         s << "'";
00544         LOG(DBG,s.str());
00545     }
00546     // pass auxiliary rule to outside
00547     createdAuxRules.push_back(auxRule);
00548 
00549     // create node and dependencies for aux rule
00550     createNodesAndIntraRuleDependenciesForRule(auxRule, createdAuxRules, hbh);
00551 
00552     // finally add aux-rule specific dependency from external atom to aux-rule
00553     Node nauxRule = getNode(auxRule);
00554     Dependency dep;
00555     bool success;
00556     DependencyInfo diExternalConstantInput;
00557     diExternalConstantInput.externalConstantInput = true;
00558     boost::tie(dep, success) = boost::add_edge(neatom, nauxRule, diExternalConstantInput, dg);
00559     assert(success);
00560 
00561     // store link to auxiliary predicate in external atom (for more comfortable model building)
00562     ExternalAtom eatomstoreback(eatom);
00563     eatomstoreback.auxInputPredicate = auxHeadPred;
00564     eatomstoreback.auxInputMapping.swap(auxInputMapping);
00565     registry->eatoms.update(eatom, eatomstoreback);
00566 }
00567 
00568 
00569 // create auxiliary rule head predicate (in registry) and return ID
00570 ID DependencyGraph::createAuxiliaryRuleHeadPredicate(ID forEAtom)
00571 {
00572     return registry->getAuxiliaryConstantSymbol('i', forEAtom);
00573 }
00574 
00575 
00576 ID DependencyGraph::createAuxiliaryRuleHead(
00577 ID idauxpred,
00578 const std::list<ID>& variables)
00579 {
00580     // create ordinary nonground atom
00581     OrdinaryAtom head(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN | ID::PROPERTY_AUX | ID::PROPERTY_EXTERNALINPUTAUX);
00582 
00583     // set tuple
00584     head.tuple.push_back(idauxpred);
00585     head.tuple.insert(head.tuple.end(), variables.begin(), variables.end());
00586 
00587     // TODO: outsource this, together with printing in HexGrammarPTToASTConverter.cpp, use iterator interface
00588     std::stringstream ss;
00589     RawPrinter printer(ss, registry);
00590     Tuple::const_iterator it = head.tuple.begin();
00591     printer.print(*it);
00592     it++;
00593     if( it != head.tuple.end() ) {
00594         ss << "(";
00595         printer.print(*it);
00596         it++;
00597         while(it != head.tuple.end()) {
00598             ss << ",";
00599             printer.print(*it);
00600             it++;
00601         }
00602         ss << ")";
00603     }
00604     head.text = ss.str();
00605 
00606                                  // onatoms.storeAndGetID(head);
00607     ID idhead = registry->storeOrdinaryAtom(head);
00608     return idhead;
00609 }
00610 
00611 
00612 ID DependencyGraph::createAuxiliaryRule(
00613 ID head, const std::list<ID>& body)
00614 {
00615     Rule r(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR | ID::PROPERTY_AUX | ID::PROPERTY_EXTERNALINPUTAUX);
00616     r.head.push_back(head);
00617     BOOST_FOREACH(ID bid, body) {
00618         r.body.push_back(bid);
00619         if( bid.isExternalAtom() )
00620             r.kind |= ID::PROPERTY_RULE_EXTATOMS;
00621     }
00622     ID id = registry->storeRule(r);
00623     return id;
00624 }
00625 
00626 
00627 // create "externalPredicateInput" dependencies
00628 void DependencyGraph::createExternalPredicateInputDependencies(
00629 const HeadBodyHelper& hbh)
00630 {
00631     LOG_SCOPE(ANALYZE,"cEPID", false);
00632     DBGLOG(DBG,"=createExternalPredicateInputDependencies");
00633 
00634     // for all external atoms:
00635     //   for all predicate inputs:
00636     //     assert that they are not variable terms
00637     //     find predicates in heads of rules that match the predicate input
00638     //     (for that we use hbh)
00639 
00640     // find all external atom nodes
00641     const NodeIDIndex& nidx = nm.get<IDTag>();
00642     for(NodeIDIndex::const_iterator itext = nidx.begin();
00643     itext != nidx.end(); ++itext) {
00644         if( !itext->id.isAtom() || !itext->id.isExternalAtom() )
00645             continue;
00646 
00647         #ifndef NDEBUG
00648         std::ostringstream os;
00649         os << "itext" << itext->id.address;
00650         DBGLOG_SCOPE(DBG,os.str(), false);
00651         DBGLOG(DBG,"=" << itext->id);
00652         #endif
00653 
00654         const ExternalAtom& eatom = registry->eatoms.getByID(itext->id);
00655         LOG(DBG,"checking external atom " << eatom);
00656 
00657         assert(!!eatom.pluginAtom);
00658         PluginAtom* pluginAtom = eatom.pluginAtom;
00659 
00660         // make sure the meta information fits the external atom
00661         // (only assert here, should be ensured by plugin loading or parsing)
00662         assert(pluginAtom->checkInputArity(eatom.inputs.size()));
00663         assert(pluginAtom->checkOutputArity(eatom.getExtSourceProperties(), eatom.tuple.size()));
00664 
00665         // find ID of all predicate input constant terms
00666         for(unsigned at = 0; at != eatom.inputs.size(); ++at) {
00667             // only consider predicate inputs
00668             if( pluginAtom->getInputType(at) != PluginAtom::PREDICATE )
00669                 continue;
00670 
00671             ID idpred = eatom.inputs[at];
00672 
00673             #ifndef NDEBUG
00674             std::ostringstream os;
00675             os << "at" << at;
00676             DBGLOG_SCOPE(DBG,os.str(), false);
00677             DBGLOG(DBG,"= checking predicate input " << idpred << " at position " << at);
00678             #endif
00679 
00680             // this input must be a constant term, nothing else allowed
00681             if( idpred.isVariableTerm() )
00682                 throw FatalError(
00683                     "external atom inputs of type 'predicate' must not be variables!"
00684                     " (got &" + pluginAtom->getPredicate() + " with variable input '" +
00685                     registry->getTermStringByID(idpred) + "')");
00686             assert(idpred.isConstantTerm());
00687             // inputMask is mutable so we may store it back this way (no index on it)
00688             eatom.inputMask->addPredicate(idpred);
00689 
00690             // here: we found a predicate input for this eatom where we need to calculate all dependencies
00691             createExternalPredicateInputDependenciesForInput(eatom.getExtSourceProperties().isNonmonotonic(at), *itext, idpred, hbh);
00692         }
00693 
00694     }                            // go through all external atom nodes
00695 }
00696 
00697 
00698 void DependencyGraph::createExternalPredicateInputDependenciesForInput(
00699 bool nonmonotonic, const NodeMappingInfo& ni_eatom, ID predicate, const HeadBodyHelper& hbh)
00700 {
00701     LOG_SCOPE(DBG,"cEPIDfI",false);
00702     LOG(DBG,"=createExternalPredicateInputDependenciesForInput "
00703         "(finding all rules with heads that use predicate " << predicate << ")");
00704 
00705     DependencyInfo diExternalPredicateInput;
00706     diExternalPredicateInput.externalPredicateInput = true;
00707     diExternalPredicateInput.externalNonmonotonicPredicateInput = nonmonotonic;
00708 
00709     const HeadBodyHelper::HeadPredicateIndex& hb_hpred = hbh.infos.get<HeadPredicateTag>();
00710     HeadBodyHelper::HeadPredicateIndex::const_iterator it, it_end;
00711     for(boost::tie(it, it_end) = hb_hpred.equal_range(predicate);
00712     it != it_end; ++it) {
00713         // found atom that matches and is in at least one rule head
00714         // (those that match and are only in body should have ID_FAIL stored in headPredicate)
00715         assert(it->inHead);
00716 
00717         LOG(DBG,"found matchin ordinary atom: " << it->id);
00718         BOOST_FOREACH( Node n, boost::join(it->inHeadOfNondisjunctiveRules, it->inHeadOfDisjunctiveRules) ) {
00719             LOG(DBG,"adding external dependency " << ni_eatom.id << " -> " << propsOf(n).id);
00720 
00721             Dependency dep;
00722             bool success;
00723             boost::tie(dep, success) = boost::add_edge(
00724                 ni_eatom.node, n, diExternalPredicateInput, dg);
00725             assert(success);
00726         }                        // iterate over rules where this atom is head
00727     }                            // iterate over atoms with matching predicate
00728 }
00729 
00730 
00731 // build all unifying dependencies ("{positive,negative}{Rule,Constraint}", "unifyingHead")
00732 void DependencyGraph::createUnifyingDependencies(
00733 const HeadBodyHelper& hbh)
00734 {
00735     createHeadHeadUnifyingDependencies(hbh);
00736     createHeadBodyUnifyingDependencies(hbh);
00737 }
00738 
00739 
00740 namespace
00741 {
00742     template<typename IteratorT, typename GraphT>
00743         void addMutualDependency(
00744         IteratorT itv1, IteratorT itv2,
00745     const DependencyGraph::DependencyInfo& di, GraphT& dg) {
00746         typename GraphT::edge_descriptor dep;
00747         bool success;
00748         boost::tie(dep, success) = boost::add_edge(*itv1, *itv2, di, dg);
00749         assert(success);
00750         boost::tie(dep, success) = boost::add_edge(*itv2, *itv1, di, dg);
00751         assert(success);
00752     }
00753 
00754     template<typename RangeT, typename GraphT>
00755         void addAllMutualDependencies(
00756         const RangeT& range1, const RangeT& range2,
00757     const DependencyGraph::DependencyInfo& di, GraphT& dg) {
00758         bool breakSymmetry = false;
00759         if( &range1 == &range2 &&
00760             range1.begin() == range2.begin() &&
00761             range1.end() == range2.end() )
00762             breakSymmetry = true;
00763         for(typename RangeT::const_iterator it1 = range1.begin();
00764         it1 != range1.end(); ++it1) {
00765             typename RangeT::const_iterator it2;
00766             if( breakSymmetry ) {
00767                 // do half cross product, do not intersect with itself
00768                 it2 = it1;
00769                 it2++;
00770             }
00771             else {
00772                 // do full cross product
00773                 it2 = range2.begin();
00774             }
00775             for(;it2 != range2.end(); ++it2) {
00776                 if( *it1 == *it2 )
00777                     continue;
00778 
00779                 addMutualDependency(it1, it2, di, dg);
00780             }
00781         }
00782     }
00783 }
00784 
00785 
00786 // helpers
00787 // "unifyingHead" dependencies
00788 void DependencyGraph::createHeadHeadUnifyingDependencies(
00789 const HeadBodyHelper& hbh)
00790 {
00791     LOG_SCOPE(ANALYZE,"cHHUD",true);
00792     DBGLOG(DBG,"=createHeadHeadUnifyingDependencies");
00793 
00794     DependencyInfo diUnifyingHead;
00795     diUnifyingHead.unifyingHead = true;
00796     DependencyInfo diUnifyingDisjunctiveHead;
00797     diUnifyingDisjunctiveHead.unifyingHead = true;
00798     diUnifyingDisjunctiveHead.disjunctive = true;
00799 
00800     // go through head body helper in two nested loops, matching inHead=true to inHead=true
00801     // iteration order does not matter
00802     // we start in the inner loop from the element after the outer loop's element
00803     // this way we can break symmetries and use half the time
00804 
00805     // we also need to create dependencies between equal elements in multiple heads
00806 
00807     const HeadBodyHelper::InHeadIndex& hb_ih = hbh.infos.get<InHeadTag>();
00808     HeadBodyHelper::InHeadIndex::const_iterator it1, it2, itend;
00809 
00810     // outer loop with it1
00811     for(boost::tie(it1, itend) = hb_ih.equal_range(true);
00812     it1 != itend; ++it1) {
00813         #ifndef NDEBUG
00814         std::ostringstream os;
00815         os << "it1:" << it1->id;
00816         DBGLOG_SCOPE(DBG,os.str(), false);
00817         #endif
00818 
00819         assert(it1->id.isAtom());
00820         assert(it1->id.isOrdinaryAtom());
00821         const OrdinaryAtom& oa1 = registry->lookupOrdinaryAtom(it1->id);
00822         DBGLOG(DBG,"= " << oa1);
00823 
00824         // create head-head dependencies between equal (same iterator, and in
00825         // that sense not unifying but trivially unifying) elements in different heads:
00826         // * disjunctive:
00827         //   * inHeadOfDisjunctiveRules <-> inHeadOfNondisjunctiveRules
00828         //   * inHeadOfDisjunctiveRules <-> inHeadOfDisjunctiveRules (but not to itself)
00829         //   * inHeadOfNondisjunctiveRules <-> inHeadOfDisjunctiveRules
00830         // * nondisjunctive:
00831         //   * inHeadOfNondisjunctiveRules <-> inHeadOfNondisjunctiveRules (but not to itself)
00832         DBGLOG(DBG,"adding unifying head-head dependency for " << oa1 <<
00833             " in head of disjunctive rules " <<
00834             printvector(it1->inHeadOfDisjunctiveRules) <<
00835             " and in head of nondisjunctive rules " <<
00836             printvector(it1->inHeadOfNondisjunctiveRules));
00837         addAllMutualDependencies(
00838             it1->inHeadOfNondisjunctiveRules, it1->inHeadOfNondisjunctiveRules,
00839             diUnifyingHead, dg);
00840         // this takes care of both directions
00841         addAllMutualDependencies(
00842             it1->inHeadOfDisjunctiveRules, it1->inHeadOfNondisjunctiveRules,
00843             diUnifyingDisjunctiveHead, dg);
00844         addAllMutualDependencies(
00845             it1->inHeadOfDisjunctiveRules, it1->inHeadOfDisjunctiveRules,
00846             diUnifyingDisjunctiveHead, dg);
00847 
00848         // inner loop with it2
00849         it2 = it1;
00850         it2++;
00851         for(;it2 != itend; ++it2) {
00852             DBGLOG(DBG,"it2:" << it2->id);
00853             assert(it2->id.isAtom());
00854             assert(it2->id.isOrdinaryAtom());
00855             const OrdinaryAtom& oa2 = registry->lookupOrdinaryAtom(it2->id);
00856             DBGLOG(DBG,"checking against " << oa2);
00857 
00858             if( !oa1.unifiesWith(oa2, registry) )
00859                 continue;
00860 
00861             // now create head-head dependencies:
00862             // * disjunctive:
00863             //   * inHeadOfDisjunctiveRules <-> inHeadOfNondisjunctiveRules
00864             //   * inHeadOfDisjunctiveRules <-> inHeadOfDisjunctiveRules
00865             //   * inHeadOfNondisjunctiveRules <-> inHeadOfDisjunctiveRules
00866             // * nondisjunctive:
00867             //   * inHeadOfNondisjunctiveRules <-> inHeadOfNondisjunctiveRules
00868 
00869             DBGLOG(DBG,"adding unifying head-head dependency between " <<
00870                 oa1 << " in head of disjunctive rules " <<
00871                 printvector(it1->inHeadOfDisjunctiveRules) <<
00872                 " and in head of nondisjunctive rules " <<
00873                 printvector(it1->inHeadOfNondisjunctiveRules) <<
00874                 " and " <<
00875                 oa2 << " in head of disjunctive rules " <<
00876                 printvector(it2->inHeadOfDisjunctiveRules) <<
00877                 " and in head of nondisjunctive rules " <<
00878                 printvector(it2->inHeadOfNondisjunctiveRules));
00879 
00880             addAllMutualDependencies(
00881                 it1->inHeadOfNondisjunctiveRules, it2->inHeadOfNondisjunctiveRules,
00882                 diUnifyingHead, dg);
00883             addAllMutualDependencies(
00884                 it1->inHeadOfDisjunctiveRules, it2->inHeadOfNondisjunctiveRules,
00885                 diUnifyingDisjunctiveHead, dg);
00886             addAllMutualDependencies(
00887                 it1->inHeadOfNondisjunctiveRules, it2->inHeadOfDisjunctiveRules,
00888                 diUnifyingDisjunctiveHead, dg);
00889             addAllMutualDependencies(
00890                 it1->inHeadOfDisjunctiveRules, it2->inHeadOfDisjunctiveRules,
00891                 diUnifyingDisjunctiveHead, dg);
00892         }                        // inner loop over atoms in heads
00893     }                            // outer loop over atoms in heads
00894 }
00895 
00896 
00897 // "{positive,negative}{Rule,Constraint}" dependencies
00898 void DependencyGraph::createHeadBodyUnifyingDependencies(
00899 const HeadBodyHelper& hbh)
00900 {
00901     LOG_SCOPE(ANALYZE,"cHBUD",true);
00902     DBGLOG(DBG,"=createHeadBodyUnifyingDependencies");
00903 
00904     DependencyInfo diPositiveRegularRule;
00905     diPositiveRegularRule.positiveRegularRule = true;
00906     DependencyInfo diPositiveConstraint;
00907     diPositiveConstraint.positiveConstraint = true;
00908     DependencyInfo diNegativeRule;
00909     diNegativeRule.negativeRule = true;
00910 
00911     // go through head body helper in two nested loops, matching inHead=true to inBody=true
00912     // iteration order does not matter
00913 
00914     const HeadBodyHelper::InHeadIndex& hb_ih = hbh.infos.get<InHeadTag>();
00915     HeadBodyHelper::InHeadIndex::const_iterator ithbegin, ithend, ith;
00916     boost::tie(ithbegin, ithend) = hb_ih.equal_range(true);
00917 
00918     const HeadBodyHelper::InBodyIndex& hb_ib = hbh.infos.get<InBodyTag>();
00919     HeadBodyHelper::InBodyIndex::const_iterator itbbegin, itbend, itb;
00920     boost::tie(itbbegin, itbend) = hb_ib.equal_range(true);
00921 
00922     // outer loop with ith on heads
00923     for(ith = ithbegin; ith != ithend; ++ith) {
00924         #ifndef NDEBUG
00925         std::ostringstream os;
00926         os << "ith:" << ith->id;
00927         DBGLOG_SCOPE(DBG,os.str(), false);
00928         #endif
00929 
00930         assert(ith->id.isAtom());
00931         assert(ith->id.isOrdinaryAtom());
00932         const OrdinaryAtom& oah = registry->lookupOrdinaryAtom(ith->id);
00933         DBGLOG(DBG,"= " << oah);
00934 
00935         // inner loop with itb on bodies
00936         for(itb = itbbegin; itb != itbend; ++itb) {
00937             // do not check for *itb == *ith! we need those dependencies
00938             DBGLOG(DBG,"itb:" << itb->id);
00939             assert(itb->id.isAtom());
00940             assert(itb->id.isOrdinaryAtom());
00941             const OrdinaryAtom& oab = registry->lookupOrdinaryAtom(itb->id);
00942             DBGLOG(DBG,"checking against " << oab);
00943 
00944             if( !oah.unifiesWith(oab, registry) )
00945                 continue;
00946 
00947             LOG(DBG,"adding head-body dependency between " <<
00948                 oah << " in head of rules " << printrange(
00949                 boost::join(ith->inHeadOfNondisjunctiveRules,
00950                 ith->inHeadOfDisjunctiveRules)) << " and " <<
00951                 oab << " in posR/posC/neg bodies " <<
00952                 printvector(itb->inPosBodyOfRegularRules) << "/" <<
00953                 printvector(itb->inPosBodyOfConstraints) << "/" <<
00954                 printvector(itb->inNegBodyOfRules));
00955 
00956             Dependency dep;
00957             bool success;
00958             BOOST_FOREACH(Node nh, boost::join(
00959             ith->inHeadOfNondisjunctiveRules, ith->inHeadOfDisjunctiveRules)) {
00960                 for(NodeList::const_iterator itnb = itb->inPosBodyOfRegularRules.begin();
00961                 itnb != itb->inPosBodyOfRegularRules.end(); ++itnb) {
00962                     // here we may remove self loops, but then we cannot check tightness (XXX can we?)
00963                     boost::tie(dep, success) = boost::add_edge(*itnb, nh, diPositiveRegularRule, dg);
00964                     assert(success);
00965                 }
00966                 for(NodeList::const_iterator itnb = itb->inPosBodyOfConstraints.begin();
00967                 itnb != itb->inPosBodyOfConstraints.end(); ++itnb) {
00968                     // no self loops possible
00969                     assert(*itnb != nh);
00970                     boost::tie(dep, success) = boost::add_edge(*itnb, nh, diPositiveConstraint, dg);
00971                     assert(success);
00972                 }
00973                 for(NodeList::const_iterator itnb = itb->inNegBodyOfRules.begin();
00974                 itnb != itb->inNegBodyOfRules.end(); ++itnb) {
00975                     // here we must not remove self loops, we may need them
00976                     boost::tie(dep, success) = boost::add_edge(*itnb, nh, diNegativeRule, dg);
00977                     assert(success);
00978                 }
00979             }                    // loop over first collection of rules
00980         }                        // inner loop over atoms in heads
00981     }                            // outer loop over atoms in heads
00982 }
00983 
00984 
00985 //
00986 // graphviz output
00987 //
00988 namespace
00989 {
00990     inline std::string graphviz_node_id(DependencyGraph::Node n) {
00991         std::ostringstream os;
00992         os << "n" << n;
00993         return os.str();
00994     }
00995 }
00996 
00997 
00998 void DependencyGraph::writeGraphVizNodeLabel(std::ostream& o, Node n, bool verbose) const
00999 {
01000     const NodeInfo& nodeinfo = getNodeInfo(n);
01001     if( verbose ) {
01002         o << "node" << n << ": " << nodeinfo.id << "\\n";
01003         RawPrinter printer(o, registry);
01004         printer.print(nodeinfo.id);
01005     }
01006     else {
01007         o << "n" << n << ":";
01008         switch(nodeinfo.id.kind >> ID::SUBKIND_SHIFT) {
01009             case 0x06: o << "ext atom"; break;
01010             case 0x30: o << "rule"; break;
01011             case 0x31: o << "constraint"; break;
01012             case 0x32: o << "weak constraint"; break;
01013             default: o << "unknown type=0x" << std::hex << (nodeinfo.id.kind >> ID::SUBKIND_SHIFT); break;
01014         }
01015         o << "/" << nodeinfo.id.address;
01016     }
01017 }
01018 
01019 
01020 void DependencyGraph::writeGraphVizDependencyLabel(std::ostream& o, Dependency dep, bool verbose) const
01021 {
01022     const DependencyInfo& di = getDependencyInfo(dep);
01023     if( verbose ) {
01024         o << di;
01025     }
01026     else {
01027         o <<
01028             (di.positiveRegularRule?" posR":"") <<
01029             (di.positiveConstraint?" posC":"") <<
01030             (di.negativeRule?" negR":"") <<
01031             (di.unifyingHead?" unifying":"") <<
01032             (di.positiveExternal?" posExt":"") <<
01033             (di.negativeExternal?" negExt":"") <<
01034             (di.externalConstantInput?" extConstInp":"") <<
01035             (di.externalPredicateInput?" extPredInp":"") <<
01036             (di.externalNonmonotonicPredicateInput?" extNonmonPredInp":"");
01037     }
01038 }
01039 
01040 
01041 // output graph as graphviz source
01042 void DependencyGraph::writeGraphViz(std::ostream& o, bool verbose) const
01043 {
01044     // boost::graph::graphviz is horribly broken!
01045     // therefore we print it ourselves
01046 
01047     o << "digraph G {" << std::endl <<
01048         "rankdir=BT;" << std::endl;// print root nodes at bottom, leaves at top!
01049 
01050     // print vertices
01051     NodeIterator it, it_end;
01052     for(boost::tie(it, it_end) = boost::vertices(dg);
01053     it != it_end; ++it) {
01054         o << graphviz_node_id(*it) << "[label=\"";
01055         {
01056             std::ostringstream ss;
01057             writeGraphVizNodeLabel(ss, *it, verbose);
01058             graphviz::escape(o, ss.str());
01059         }
01060         o << "\"";
01061         if( getNodeInfo(*it).id.isRule() )
01062             o << ",shape=box";
01063         o << "];" << std::endl;
01064     }
01065 
01066     // print edges
01067     DependencyIterator dit, dit_end;
01068     for(boost::tie(dit, dit_end) = boost::edges(dg);
01069     dit != dit_end; ++dit) {
01070         Node src = sourceOf(*dit);
01071         Node target = targetOf(*dit);
01072         o << graphviz_node_id(src) << " -> " << graphviz_node_id(target) <<
01073             "[label=\"";
01074         {
01075             std::ostringstream ss;
01076             writeGraphVizDependencyLabel(o, *dit, verbose);
01077             graphviz::escape(o, ss.str());
01078         }
01079         o << "\"];" << std::endl;
01080     }
01081 
01082     o << "}" << std::endl;
01083 }
01084 
01085 
01086 DLVHEX_NAMESPACE_END
01087 
01088 
01089 // vim:expandtab:ts=4:sw=4:
01090 // mode: C++
01091 // End: