dlvhex
2.5.0
|
00001 /* dlvhex -- Answer-Set Programming with external interfaces. 00002 * Copyright (C) 2005-2007 Roman Schindlauer 00003 * Copyright (C) 2006-2015 Thomas Krennwallner 00004 * Copyright (C) 2009-2016 Peter Schüller 00005 * Copyright (C) 2011-2016 Christoph Redl 00006 * Copyright (C) 2015-2016 Tobias Kaminski 00007 * Copyright (C) 2015-2016 Antonius Weinzierl 00008 * 00009 * This file is part of dlvhex. 00010 * 00011 * dlvhex is free software; you can redistribute it and/or modify it 00012 * under the terms of the GNU Lesser General Public License as 00013 * published by the Free Software Foundation; either version 2.1 of 00014 * the License, or (at your option) any later version. 00015 * 00016 * dlvhex is distributed in the hope that it will be useful, but 00017 * WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with dlvhex; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00024 * 02110-1301 USA. 00025 */ 00026 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: