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 00034 #ifdef HAVE_CONFIG_H 00035 #include "config.h" 00036 #endif // HAVE_CONFIG_H 00037 00038 #include "dlvhex2/ComponentGraph.h" 00039 #include "dlvhex2/Logger.h" 00040 #include "dlvhex2/Printer.h" 00041 #include "dlvhex2/ProgramCtx.h" 00042 #include "dlvhex2/Registry.h" 00043 #include "dlvhex2/GraphvizHelpers.h" 00044 00045 #include <boost/graph/breadth_first_search.hpp> 00046 #include <boost/graph/two_bit_color_map.hpp> 00047 #include <boost/graph/strong_components.hpp> 00048 #include <boost/graph/graph_utility.hpp> 00049 #include <boost/graph/subgraph.hpp> 00050 #include <boost/property_map/property_map.hpp> 00051 #include <boost/algorithm/string/replace.hpp> 00052 #include <boost/range/iterator_range.hpp> 00053 #include <boost/foreach.hpp> 00054 00055 #include <sstream> 00056 00057 DLVHEX_NAMESPACE_BEGIN 00058 00059 namespace 00060 { 00061 typedef DependencyGraph::Node Node; 00062 typedef std::set<Node> NodeSet; 00063 typedef std::vector<Node> NodeVector; 00064 00065 typedef ComponentGraph::ComponentSet ComponentSet; 00066 typedef ComponentGraph::ComponentInfo ComponentInfo; 00067 typedef ComponentGraph::DependencyInfo DependencyInfo; 00068 typedef ComponentGraph::DepMap DepMap; 00069 } 00070 00071 00072 const ComponentGraph::DependencyInfo& 00073 ComponentGraph::DependencyInfo::operator|=( 00074 const ComponentGraph::DependencyInfo& other) 00075 { 00076 DependencyGraph::DependencyInfo::operator|=(other); 00077 00078 BOOST_FOREACH (DepEdge de, other.depEdges) depEdges.push_back(de); 00079 00080 // depEdges.insert(depEdges.end(), other.depEdges.begin(), other.depEdges.end()); 00081 00082 return *this; 00083 } 00084 00085 00086 std::ostream& ComponentGraph::ComponentInfo::print(std::ostream& o) const 00087 { 00088 if( !outerEatoms.empty() ) 00089 o << "outerEatoms: " << printrange(outerEatoms) << std::endl; 00090 if( !innerRules.empty() ) 00091 o << "innerRules: " << printrange(innerRules) << std::endl; 00092 if( !innerEatoms.empty() ) 00093 o << "innerEatoms: " << printrange(innerEatoms) << std::endl; 00094 if( !innerConstraints.empty() ) 00095 o << "innerConstraints: " << printrange(innerConstraints) << std::endl; 00096 return o; 00097 } 00098 00099 00100 std::ostream& ComponentGraph::DependencyInfo::print(std::ostream& o) const 00101 { 00102 return o << static_cast<const DependencyGraph::DependencyInfo&>(*this); 00103 } 00104 00105 00106 ComponentGraph::ComponentGraph(const DependencyGraph& dg, ProgramCtx& ctx, RegistryPtr reg): 00107 ctx(ctx), 00108 reg(reg), 00109 #ifdef COMPGRAPH_SOURCESDEBUG 00110 dg(dg), 00111 #endif 00112 cg() 00113 { 00114 DBGLOG(DBG, "Building component graph"); 00115 calculateComponents(dg); 00116 } 00117 00118 00119 ComponentGraph::~ComponentGraph() 00120 { 00121 DBGLOG(DBG, "Destructing component graph " << this); 00122 } 00123 00124 00125 WARNING("fixed point: eatoms only in positive cycles is misleading, we demand only positive cycles for all atoms! (no negative or disjunctive edge at all)") 00126 #if 0 00127 namespace 00128 { 00129 typedef unsigned Polarity; 00130 static const Polarity PUNSET = 0x00; 00131 static const Polarity PPOS = 0x01; 00132 static const Polarity PNEG = 0x10; 00133 static const Polarity PBOTH = 0x11; 00134 typedef boost::vector_property_map<Polarity> PolarityPropertyMap; 00135 00136 template<typename Graph> 00137 class PolarityMarkingVisitor: 00138 public boost::default_bfs_visitor 00139 { 00140 public: 00141 typedef typename Graph::vertex_descriptor Vertex; 00142 typedef typename Graph::edge_descriptor Edge; 00143 00144 public: 00145 PolarityMarkingVisitor(PolarityPropertyMap& ppm): 00146 ppm(ppm) {} 00147 00154 void examine_edge(Edge e, const Graph& g) const 00155 { 00156 Vertex from = boost::source(e, g); 00157 Vertex to = boost::target(e, g); 00158 DBGLOG(DBG,"examining edge from " << from << " to " << to); 00159 00160 const DependencyGraph::DependencyInfo& di = g[e]; 00161 WARNING("TODO review this decision criteria (not sure if "defensive" stuff is necessary)") 00162 bool negative = 00163 di.negativeRule | 00164 di.negativeExternal | 00165 di.unifyingHead;//defensive reasoning 00166 bool positive = 00167 di.positiveRegularRule | 00168 di.positiveConstraint | 00169 di.unifyingHead |//defensive reasoning 00170 di.positiveExternal | 00171 //defensive reasoning 00172 di.externalConstantInput | 00173 //defensive reasoning 00174 di.externalPredicateInput; 00175 Polarity p = ppm[from]; 00176 assert(((p == PPOS) || (p == PNEG)) && 00177 "PUNSET means we did not correctly set the last vertex, " 00178 "PBOTH means we did not abort but we should have"); 00179 Polarity pto = ppm[to]; 00180 DBGLOG(DBG," edge is " << 00181 (positive?"positive":"") << (negative?"negative":"") << 00182 " with frompolarity " << p << " and topolarity " << pto); 00183 if( positive ) 00184 pto |= p; 00185 if( negative ) 00186 // XOR 0x11 -> invert both bits 00187 pto |= (p ^ 0x11); 00188 DBGLOG(DBG,"result polarity is " << pto); 00189 if( pto == PBOTH ) { 00190 throw std::string("negcycle"); 00191 } 00192 ppm[to] = pto; 00193 } 00194 00195 protected: 00196 PolarityPropertyMap& ppm; 00197 }; 00198 00199 /* 00200 * strategy for calculation: 00201 * * initialize a property map 00202 * * for each inner eatom with ID "id" 00203 * * init nodesToCheck with PUNSET 00204 * * do a bfs visit, never leaving nodesToCheck: 00205 * * for initial vertex: mark as POS 00206 * * for each discovered edge (u,v): 00207 * mark v like u if edge positive (add mark) 00208 * mark v different from u if edge negative (add mark) 00209 * if initial node contain both marks -> return false 00210 * * return true 00211 */ 00212 bool checkEatomsOnlyInPositiveCycles( 00213 const DependencyGraph& dg, 00214 const NodeSet& nodesToCheck, 00215 const NodeVector& innerEatomNodes) { 00216 DBGLOG_SCOPE(DBG,"cEOiPC",false); 00217 LOG(DBG,"checking whether eatoms in nodes " << 00218 printrange(innerEatomNodes) << 00219 " are only in positive cycles within SCC of nodes " << 00220 printrange(nodesToCheck)); 00221 00222 // init polarity map with size 00223 DBGLOG(DBG,"initializing property maps"); 00224 PolarityPropertyMap 00225 ppm(dg.countNodes()); 00226 // init color map with size 00227 boost::two_bit_color_map<boost::identity_property_map> 00228 cmap(dg.countNodes()); 00229 00230 // init black color (=do not touch) for nodes not in nodesToCheck 00231 DependencyGraph::NodeIterator it, it_end; 00232 for(boost::tie(it, it_end) = dg.getNodes(); 00233 it != it_end; ++it) { 00234 if( nodesToCheck.find(*it) == nodesToCheck.end() ) { 00235 boost::put(cmap, *it, boost::two_bit_black); 00236 } 00237 } 00238 00239 try 00240 { 00241 BOOST_FOREACH(const Node eatomNode, innerEatomNodes) { 00242 DBGLOG(DBG,"checking for eatom node " << eatomNode); 00243 // init color and polarity map for nodes in nodesToCheck 00244 BOOST_FOREACH(const Node n, nodesToCheck) { 00245 boost::put(ppm, n, PUNSET); 00246 boost::put(cmap, n, boost::two_bit_white); 00247 } 00248 00249 // startnode is positive 00250 ppm[eatomNode] = PPOS; 00251 00252 PolarityMarkingVisitor<DependencyGraph::Graph> 00253 vis(ppm); 00254 00255 // the lousy named parameters simply won't compile and are not documented properly 00256 boost::breadth_first_visit( 00257 dg.getInternalGraph(), eatomNode, 00258 visitor(vis). 00259 color_map(cmap)); 00260 } 00261 } 00262 catch(const std::string& s) { 00263 if( s == "negcycle" ) { 00264 LOG(DBG,"found negative cycle!"); 00265 return false; 00266 } 00267 else 00268 throw; 00269 } 00270 00271 return true; 00272 } 00273 00274 } // namespace {} 00275 #endif 00276 00277 namespace 00278 { 00279 /* 00280 * strategy for calculation: 00281 * * iterate through all nodes in nodesToCheck 00282 * * iterate through outgoing edges 00283 * * if negative and leading to node in nodesToCheck return false 00284 * * return true 00285 */ 00286 bool checkNoNegativeEdgesInComponent( 00287 const DependencyGraph& dg, 00288 const NodeSet& nodesToCheck) { 00289 DBGLOG_SCOPE(DBG,"cNNEiC",false); 00290 BOOST_FOREACH(Node n, nodesToCheck) { 00291 DBGLOG(DBG,"checking predecessor edges of node " << n); 00292 DependencyGraph::PredecessorIterator it, it_end; 00293 for(boost::tie(it, it_end) = dg.getDependencies(n); 00294 it != it_end; ++it) { 00295 const DependencyGraph::DependencyInfo& di = dg.propsOf(*it); 00296 if( di.negativeRule | di.negativeExternal | di.disjunctive ) { 00297 // found neg dependency, check if it is within SCC 00298 Node pnode = dg.targetOf(*it); 00299 if( nodesToCheck.find(pnode) != nodesToCheck.end() ) { 00300 DBGLOG(DBG,"found negative/disjunctive dependency to node " << pnode << " -> not wellfounded"); 00301 return false; 00302 } 00303 } 00304 } 00305 } 00306 00307 return true; 00308 } 00309 00310 bool checkEatomMonotonic( 00311 RegistryPtr reg, 00312 ID eatomid) { 00313 DBGLOG(DBG,"checking whether eatom " << eatomid << " is monotonic"); 00314 00315 // check monotonicity 00316 const ExternalAtom& eatom = reg->eatoms.getByID(eatomid); 00317 assert(!!eatom.pluginAtom); 00318 PluginAtom* pa = eatom.pluginAtom; 00319 if( eatom.getExtSourceProperties().isMonotonic() ) { 00320 // if( pa->isMonotonic() ) 00321 DBGLOG(DBG," eatom " << eatomid << " is monotonic"); 00322 return true; 00323 } 00324 else { 00325 DBGLOG(DBG," eatom " << eatomid << " is nonmonotonic"); 00326 return false; 00327 } 00328 } 00329 } 00330 00331 00332 void ComponentGraph::calculateComponents(const DependencyGraph& dg) 00333 { 00334 LOG_SCOPE(ANALYZE,"cCs",true); 00335 DBGLOG(ANALYZE,"=calculateComponents"); 00336 00337 typedef DependencyGraph::Node Node; 00338 00339 // 00340 // calculate SCCs 00341 // 00342 typedef std::vector<int> ComponentMap; 00343 ComponentMap scc; 00344 // resize property map 00345 scc.resize(dg.countNodes()); 00346 // do it with boost 00347 unsigned scccount = boost::strong_components(dg.getInternalGraph(), boost::make_iterator_property_map(scc.begin(), get(boost::vertex_index, dg.getInternalGraph()))); 00348 LOG(ANALYZE,"boost::strong_components created " << scccount << " components"); 00349 00350 // 00351 // calculate set of nodes for each SCC: sccMembers 00352 // 00353 typedef std::vector<NodeSet> SCCMap; 00354 SCCMap sccMembers; 00355 sccMembers.resize(scccount); 00356 for(unsigned n = 0; n < dg.countNodes(); ++n) { 00357 // get the component id from scc[n] 00358 // add the node id to the set of nodes of this component 00359 sccMembers[scc[n]].insert(static_cast<Node>(n)); 00360 } 00361 00362 // 00363 // create one component for each SCC 00364 // 00365 typedef std::vector<Component> SCCToComponentMap; 00366 SCCToComponentMap sccToComponent(scccount); 00367 for(unsigned s = 0; s < scccount; ++s) { 00368 const NodeSet& nodes = sccMembers[s]; 00369 00370 Component c = boost::add_vertex(cg); 00371 DBGLOG(DBG,"created component node " << c << " for scc " << s << 00372 " with depgraph nodes " << printrange(nodes)); 00373 bool multimember = nodes.size() > 1; 00374 NodeVector innerEatomNodes; 00375 sccToComponent[s] = c; 00376 ComponentInfo& ci = propsOf(c); 00377 // assume it's monotonic 00378 ci.componentIsMonotonic = true; 00379 00380 // collect rule and eatom ids in scc 00381 for(NodeSet::const_iterator itn = nodes.begin(); 00382 itn != nodes.end(); ++itn) { 00383 #ifdef COMPGRAPH_SOURCESDEBUG 00384 ci.sources.push_back(*itn); 00385 #endif 00386 ID id = dg.propsOf(*itn).id; 00387 if( id.isRule() ) { 00388 if( id.isRegularRule() ) { 00389 ci.innerRules.push_back(id); 00390 if( id.isRuleDisjunctive() ) { 00391 ci.disjunctiveHeads = true; 00392 } 00393 } 00394 else if( id.isConstraint() || id.isWeakConstraint() ) { 00395 ci.innerConstraints.push_back(id); 00396 } 00397 else { 00398 assert(false); 00399 } 00400 00401 // check if the rule uses default negation 00402 const Rule& r = reg->rules.getByID(id); 00403 BOOST_FOREACH (ID b, r.body) { 00404 if (b.isNaf()) ci.componentIsMonotonic = false; 00405 } 00406 } 00407 else if( id.isExternalAtom() ) { 00408 // if the SCC contains more than one node, and it contains external atoms, 00409 // then they are inner external atoms (there must be some loop) 00410 if( multimember || ctx.config.getOption("NoOuterExternalAtoms") ) { 00411 ci.innerEatoms.push_back(id); 00412 innerEatomNodes.push_back(*itn); 00413 00414 if( ci.innerEatomsNonmonotonic == false ) { 00415 // check, if the newly added inner eatom is monotonic 00416 ID eatomid = dg.propsOf(*itn).id; 00417 if( !checkEatomMonotonic(reg, eatomid) ) { 00418 ci.innerEatomsNonmonotonic = true; 00419 } 00420 } 00421 } 00422 else { 00423 ci.outerEatoms.push_back(id); 00424 00425 if( ci.outerEatomsNonmonotonic == false ) { 00426 // check, if the newly added inner eatom is monotonic 00427 ID eatomid = dg.propsOf(*itn).id; 00428 if( !checkEatomMonotonic(reg, eatomid) ) { 00429 ci.outerEatomsNonmonotonic = true; 00430 } 00431 } 00432 } 00433 } 00434 else { 00435 assert(false); 00436 } 00437 } 00438 00439 // check, if the component contains only positive cycles 00440 if( !checkNoNegativeEdgesInComponent(dg, nodes) ) { 00441 ci.negativeDependencyBetweenRules = true; 00442 } 00443 00444 // components are never monotonic if they contain disjunctions or nonmonotonic external atoms 00445 if (ci.disjunctiveHeads || ci.innerEatomsNonmonotonic || ci.outerEatomsNonmonotonic) { 00446 ci.componentIsMonotonic = false; 00447 } 00448 00449 // compute if this component has a fixed domain 00450 ci.fixedDomain = calculateFixedDomain(ci); 00451 00452 // check, if the component contains recursive aggregates 00453 // Note: this also includes aggregates which depend on predicates defined in the component, even if there is no cyclic dependency! 00454 ci.recursiveAggregates = computeRecursiveAggregatesInComponent(ci); 00455 00456 // recursive aggregates in the initial component graph are disallowed 00457 // however, they might occur after collapsing components because then they are not strictly recursive (see above) 00458 if (ci.recursiveAggregates && !ctx.config.getOption("AllowAggCycles")) throw GeneralError("Program contains recursive aggregates"); 00459 00460 // compute stratification of default-negated literals and predicate input parameters 00461 calculateStratificationInfo(reg, ci); 00462 00463 // compute all predicate which occur in this component 00464 calculatePredicatesOfComponent(reg, ci); 00465 00466 DBGLOG(DBG,"-> outerEatoms " << printrange(ci.outerEatoms)); 00467 DBGLOG(DBG,"-> innerRules " << printrange(ci.innerRules)); 00468 DBGLOG(DBG,"-> innerConstraints " << printrange(ci.innerConstraints)); 00469 DBGLOG(DBG,"-> innerEatoms " << printrange(ci.innerEatoms)); 00470 DBGLOG(DBG,"-> disjunctiveHeads=" << ci.disjunctiveHeads << 00471 " negativeDependencyBetweenRules=" << ci.negativeDependencyBetweenRules << 00472 " innerEatomsNonmonotonic=" << ci.innerEatomsNonmonotonic << 00473 " outerEatomsNonmonotonic=" << ci.outerEatomsNonmonotonic << 00474 " componentIsMonotonic=" << ci.componentIsMonotonic); 00475 00476 assert(( ci.outerEatoms.empty() || 00477 (ci.innerRules.empty() && ci.innerConstraints.empty() && ci.innerEatoms.empty())) && 00478 "components with outer eatoms may not contain anything else"); 00479 } 00480 00481 WARNING("TODO if we have just one disjunctive rule inside, we can no longer use fixpoint calculation with inner eatoms, even if they are monotonic and we have only positive cycles ... ci.innerEatomsMonotonicAndOnlyPositiveCycles = false;") 00482 00483 // 00484 // create dependencies between components (now that all of them exist) 00485 // 00486 for(unsigned s = 0; s < scccount; ++s) { 00487 const NodeSet& nodes = sccMembers[s]; 00488 Component c = sccToComponent[s]; 00489 00490 // look at out-dependencies only 00491 // (successors will find and create all dependencies to this SCC) 00492 for(NodeSet::const_iterator itn = nodes.begin(); 00493 itn != nodes.end(); ++itn) { 00494 DependencyGraph::PredecessorIterator it, it_end; 00495 for(boost::tie(it, it_end) = dg.getDependencies(*itn); 00496 it != it_end; ++it) { 00497 DependencyGraph::Dependency dep = *it; 00498 Node targetnode = dg.targetOf(dep); 00499 unsigned targetscc = scc[targetnode]; 00500 if( targetscc == s ) { 00501 // dependency within SCC 00502 continue; 00503 } 00504 00505 Component targetc = sccToComponent[targetscc]; 00506 // dependency to other SCC -> store 00507 DBGLOG(DBG,"found dependency from SCC " << s << " to SCC " << targetscc); 00508 00509 // create/update dependency 00510 Dependency newdep; 00511 bool success; 00512 00513 // use dependencyinfo from original dependency 00514 //const DependencyGraph::NodeInfo& ni = dg.propsOf(targetnode); 00515 const DependencyGraph::DependencyInfo& di = dg.propsOf(dep); 00516 00517 boost::tie(newdep, success) = boost::add_edge(c, targetc, DependencyInfo(di), cg); 00518 if( !success ) { 00519 boost::tie(newdep, success) = boost::edge(c, targetc, cg); 00520 assert(success); 00521 // update existing dependency 00522 propsOf(newdep) |= di; 00523 } 00524 00525 // store original dependencies from the DependencyGraph 00526 const DependencyGraph::NodeInfo& ni1 = dg.getNodeInfo(dg.sourceOf(dep)); 00527 const DependencyGraph::NodeInfo& ni2 = dg.getNodeInfo(dg.targetOf(dep)); 00528 propsOf(newdep).depEdges.push_back(DependencyInfo::DepEdge(ni1.id, ni2.id, di)); 00529 00530 } // for each dependency of *itn 00531 } // collect dependencies outgoing from node *itn in SCC s 00532 } // create dependencies outgoing from SCC s 00533 00534 // collapse components with predecessors which contain only external atoms 00535 if (ctx.config.getOption("NoOuterExternalAtoms")){ 00536 ComponentIterator cit = getComponents().first; 00537 while(cit != getComponents().second) { 00538 bool restart = false; 00539 // for predecessors 00540 ComponentGraph::PredecessorIterator pit, pit_end; 00541 for (boost::tie(pit, pit_end) = getDependencies(*cit); pit != pit_end; ++pit) { 00542 Component dependsOn = targetOf(*pit); 00543 if (getComponentInfo(dependsOn).innerConstraints.size() == 0 && getComponentInfo(dependsOn).innerRules.size() == 0 00544 && getComponentInfo(dependsOn).innerEatoms.size() == 1 && getComponentInfo(dependsOn).outerEatoms.size() == 0){ 00545 00546 ComponentSet collapse; 00547 collapse.insert(*cit); 00548 collapse.insert(dependsOn); 00549 collapseComponents(collapse); 00550 restart = true; 00551 break; 00552 } 00553 } 00554 if (restart) cit = getComponents().first; 00555 else cit++; 00556 } 00557 } 00558 } 00559 00560 00561 bool ComponentGraph::calculateFixedDomain(ComponentInfo& ci) const 00562 { 00563 DBGLOG(DBG, "calculateFixedDomain"); 00564 00565 bool fd = true; 00566 00567 // pure external components have only a fixed domain if the output of all outer external atoms 00568 // contains no variables 00569 if (ci.innerRules.empty() && !ci.outerEatoms.empty()) { 00570 BOOST_FOREACH (ID eaid, ci.outerEatoms) { 00571 const ExternalAtom& ea = reg->eatoms.getByID(eaid); 00572 BOOST_FOREACH (ID ot, ea.tuple) { 00573 if (ot.isVariableTerm()) return false; 00574 } 00575 } 00576 return true; 00577 } 00578 00579 // get rule heads here 00580 // here we store the full atom IDs (we need to unify, the predicate is not sufficient) 00581 std::set<ID> headAtomIDs; 00582 // we only consider inner rules (constraints have no heads) 00583 BOOST_FOREACH(ID rid, ci.innerRules) { 00584 const Rule& rule = reg->rules.getByID(rid); 00585 00586 BOOST_FOREACH(ID hid, rule.head) { 00587 if( !hid.isOrdinaryAtom() ) { 00588 continue; 00589 } 00590 headAtomIDs.insert(hid); 00591 } 00592 } 00593 00594 // now check output variables 00595 00596 // here we need to check inner rules and inner constraints 00597 std::vector<ID>* ruleSets[] = {&ci.innerRules, &ci.innerConstraints}; 00598 BOOST_FOREACH (std::vector<ID>* ruleSet, ruleSets) { 00599 BOOST_FOREACH(ID rid, *ruleSet) { 00600 if( !rid.doesRuleContainExtatoms() ) { 00601 continue; 00602 } 00603 00604 const Rule& rule = reg->rules.getByID(rid); 00605 00606 // find all variable outputs in all eatoms in this rule's body 00607 std::set<ID> varsToCheck; 00608 BOOST_FOREACH(ID lid, rule.body) { 00609 if( !lid.isExternalAtom() ) 00610 continue; 00611 00612 const ExternalAtom& eatom = reg->eatoms.getByID(lid); 00613 BOOST_FOREACH(ID tid, eatom.tuple) { 00614 if( tid.isVariableTerm() ) 00615 varsToCheck.insert(tid); 00616 } 00617 } 00618 00619 // for each variable: 00620 // if it is part of a positive body atom of r 00621 // and this positive body atom of r does not unify with any rule head in c 00622 // then e is safe 00623 BOOST_FOREACH(ID vid, varsToCheck) { 00624 // check strong safety of variable vid 00625 DBGLOG(DBG,"checking fixed domain of variable " << 00626 printToString<RawPrinter>(vid,reg)); 00627 00628 bool variableSafe = false; 00629 BOOST_FOREACH(ID lid, rule.body) { 00630 // skip negative bodies 00631 if( lid.isNaf() ) 00632 continue; 00633 00634 // skip external atoms, 00635 // they could but cannot in general be assumed to limit the domain 00636 // (and that's the reason we need to check strong safety) 00637 if( lid.isExternalAtom() ) 00638 continue; 00639 00640 // skip non-ordinary atoms 00641 WARNING("can we use aggregates to limit the domain for strong safety?") 00642 WARNING("can we use builtin atoms to limit the domain for strong safety?") 00643 if( lid.isAggregateAtom() || 00644 lid.isBuiltinAtom() ) 00645 continue; 00646 00647 assert(lid.isOrdinaryAtom()); 00648 00649 // check if this body literal contains the variable 00650 // and does not unify with any head 00651 // (only then the variable is safe) 00652 bool containsVariable = false; 00653 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(lid); 00654 assert(!oatom.tuple.empty()); 00655 Tuple::const_iterator itv = oatom.tuple.begin(); 00656 itv++; 00657 while( itv != oatom.tuple.end() ) { 00658 if( *itv == vid ) { 00659 containsVariable = true; 00660 break; 00661 } 00662 itv++; 00663 } 00664 00665 if( !containsVariable ) { 00666 continue; 00667 } 00668 00669 // oatom 'oatom' was retrieved using ID 'lid' 00670 DBGLOG(DBG,"checking unifications of body literal " << 00671 printToString<RawPrinter>(lid, reg) << " with component rule heads"); 00672 bool doesNotUnify = true; 00673 BOOST_FOREACH(ID hid, headAtomIDs) { 00674 DBGLOG(DBG,"checking against " << 00675 printToString<RawPrinter>(hid, reg)); 00676 assert(hid.isOrdinaryAtom()); 00677 const OrdinaryAtom& hoatom = reg->lookupOrdinaryAtom(hid); 00678 if( oatom.unifiesWith(hoatom) ) { 00679 DBGLOG(DBG,"unification successful " 00680 "-> literal does not limit the domain"); 00681 doesNotUnify = false; 00682 break; 00683 } 00684 } 00685 00686 if( doesNotUnify ) { 00687 DBGLOG(DBG, "variable safe!"); 00688 variableSafe = true; 00689 break; 00690 } 00691 } 00692 00693 if( !variableSafe ) { 00694 // check if the variable occurs in an external atom with unstratified nonmonotonic parameters 00695 bool nonmonotonicEA = false; 00696 BOOST_FOREACH(ID lid, rule.body) { 00697 if(!lid.isNaf() && lid.isExternalAtom()) { 00698 if (ci.stratifiedLiterals.find(rid) == ci.stratifiedLiterals.end() || 00699 std::find(ci.stratifiedLiterals.at(rid).begin(), ci.stratifiedLiterals.at(rid).end(), lid) == ci.stratifiedLiterals.at(rid).end()) { 00700 nonmonotonicEA = true; 00701 break; 00702 } 00703 } 00704 } 00705 00706 fd = false; 00707 } 00708 else { 00709 DBGLOG(DBG, "Variable " << vid << " is strongly safe in rule " << rid << " (" << &ci << ")"); 00710 ci.stronglySafeVariables[rid].insert(vid); 00711 } 00712 } 00713 } 00714 } 00715 return fd; 00716 } 00717 00718 00719 bool ComponentGraph::computeRecursiveAggregatesInComponent(ComponentInfo& ci) const 00720 { 00721 // get all head predicates 00722 std::set<ID> headPredicates; 00723 BOOST_FOREACH (ID ruleID, ci.innerRules) { 00724 const Rule& rule = reg->rules.getByID(ruleID); 00725 BOOST_FOREACH (ID h, rule.head) { 00726 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(h); 00727 headPredicates.insert(oatom.tuple[0]); 00728 } 00729 } 00730 00731 // go through all aggregate atoms 00732 std::set<ID> aatoms; 00733 BOOST_FOREACH (ID ruleID, ci.innerRules) { 00734 const Rule& rule = reg->rules.getByID(ruleID); 00735 BOOST_FOREACH (ID b, rule.body) { 00736 if (b.isAggregateAtom()) { 00737 aatoms.insert(b); 00738 } 00739 } 00740 } 00741 00742 // recursively check if the aggregate depend on head atoms from this component 00743 while (!aatoms.empty()) { 00744 const AggregateAtom& aatom = reg->aatoms.getByID(*aatoms.begin()); 00745 aatoms.erase(*aatoms.begin()); 00746 BOOST_FOREACH (ID b, aatom.literals) { 00747 if (b.isOrdinaryAtom()) { 00748 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(b); 00749 if (headPredicates.find(oatom.tuple[0]) != headPredicates.end()) return true; 00750 } 00751 if (b.isExternalAtom()) { 00752 const ExternalAtom& eatom = reg->eatoms.getByID(b); 00753 int i = 0; 00754 BOOST_FOREACH (ID inp, eatom.inputs) { 00755 if (eatom.pluginAtom->getInputType(i) == PluginAtom::PREDICATE && headPredicates.find(eatom.inputs[0]) != headPredicates.end()) return true; 00756 i++; 00757 } 00758 } 00759 if (b.isAggregateAtom()) { 00760 aatoms.insert(b); 00761 } 00762 } 00763 } 00764 00765 return false; 00766 } 00767 00768 00769 void ComponentGraph::calculateStratificationInfo(RegistryPtr reg, ComponentInfo& ci) 00770 { 00771 DBGLOG(DBG, "calculateStratificationInfo"); 00772 00773 // get the head atoms of all rules in this component 00774 std::set<ID> headAtomIDs; 00775 BOOST_FOREACH(ID rid, ci.innerRules) { 00776 const Rule& rule = reg->rules.getByID(rid); 00777 00778 BOOST_FOREACH(ID hid, rule.head) { 00779 if (!hid.isOrdinaryAtom()) continue; 00780 headAtomIDs.insert(hid); 00781 00782 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(hid); 00783 ci.predicatesDefinedInComponent.insert(oatom.tuple[0]); 00784 } 00785 } 00786 00787 // for all default-negated literals and predicate input parameters in this component 00788 BOOST_FOREACH(ID rid, ci.innerRules) { 00789 const Rule& rule = reg->rules.getByID(rid); 00790 00791 BOOST_FOREACH(ID bid, rule.body) { 00792 // default-negated literals 00793 if (!bid.isExternalAtom() && bid.isNaf() && bid.isOrdinaryAtom()) { 00794 // does it unify with a head atom in this component? 00795 bool stratified = true; 00796 BOOST_FOREACH (ID hid, headAtomIDs) { 00797 const OrdinaryAtom& boatom = reg->lookupOrdinaryAtom(bid); 00798 const OrdinaryAtom& hoatom = reg->lookupOrdinaryAtom(hid); 00799 if (boatom.unifiesWith(hoatom)) { 00800 stratified = false; 00801 break; 00802 } 00803 } 00804 if (stratified) { 00805 ci.stratifiedLiterals[rid].insert(bid); 00806 } 00807 } 00808 // predicate input parameters 00809 if (bid.isExternalAtom() && !bid.isNaf()) { 00810 const ExternalAtom& eatom = reg->eatoms.getByID(bid); 00811 bool stratified = true; 00812 for (uint32_t p = 0; p < eatom.inputs.size() && stratified; ++p) { 00813 if (eatom.pluginAtom->getInputType(p) == PluginAtom::PREDICATE && eatom.getExtSourceProperties().isNonmonotonic(p)) { 00814 // is this predicate defined in this component? 00815 if (ci.predicatesDefinedInComponent.count(eatom.inputs[p]) > 0) { 00816 stratified = false; 00817 break; 00818 } 00819 } 00820 } 00821 if (stratified) { 00822 DBGLOG(DBG, "Literal " << bid << " in rule " << rid << " is stratified"); 00823 ci.stratifiedLiterals[rid].insert(bid); 00824 } 00825 } 00826 } 00827 } 00828 } 00829 00830 void ComponentGraph::calculatePredicatesOfComponent(RegistryPtr reg, ComponentInfo& ci) 00831 { 00832 DBGLOG(DBG, "calculatePredicatesOfComponent"); 00833 00834 for (int setc = 1; setc <= 2; setc++) { // do this for inner rules and inner constraints 00835 std::vector<ID>& set = (setc == 1 ? ci.innerRules : ci.innerConstraints); 00836 00837 BOOST_FOREACH(ID rid, set) { 00838 const Rule& rule = reg->rules.getByID(rid); 00839 00840 BOOST_FOREACH(ID hid, rule.head) { 00841 if (!hid.isOrdinaryAtom()) continue; 00842 ci.predicatesOccurringInComponent.insert(reg->lookupOrdinaryAtom(hid).tuple[0]); 00843 } 00844 00845 BOOST_FOREACH(ID bid, rule.body) { 00846 if (bid.isOrdinaryAtom()) { 00847 ci.predicatesOccurringInComponent.insert(reg->lookupOrdinaryAtom(bid).tuple[0]); 00848 } 00849 if (bid.isExternalAtom()) { 00850 const ExternalAtom& eatom = reg->eatoms.getByID(bid); 00851 for (int i = 0; i < eatom.inputs.size(); i++){ 00852 if (eatom.pluginAtom->getInputType(i) == PluginAtom::PREDICATE) ci.predicatesOccurringInComponent.insert(eatom.inputs[i]); 00853 } 00854 } 00855 if (bid.isAggregateAtom()) { 00856 const AggregateAtom& aatom = reg->aatoms.getByID(bid); 00857 BOOST_FOREACH (ID abid, aatom.literals){ 00858 if (abid.isOrdinaryAtom()) { 00859 ci.predicatesOccurringInComponent.insert(reg->lookupOrdinaryAtom(abid).tuple[0]); 00860 } 00861 } 00862 } 00863 } 00864 } 00865 } 00866 } 00867 00868 // Compute the dependency infos and component info 00869 // before putting components `comps' and `sharedcomps' into a new component. 00870 // 00871 // `sharedcomps' may only contain components with constraints that can be shared. 00872 // 00873 // This method does not change the graph, it only changes the output arguments, 00874 // hence it is const (and should stay const). 00875 // 00876 // This method throws an exception if this operation makes the DAG cyclic. 00877 void ComponentGraph::computeCollapsedComponentInfos( 00878 const ComponentSet& comps, const ComponentSet& sharedcomps, 00879 DepMap& newIncomingDependencies, DepMap& newOutgoingDependencies, 00880 // see comment above about const! 00881 ComponentInfo& newComponentInfo) const 00882 { 00883 DBGLOG_SCOPE(DBG,"cCCI", false); 00884 DBGLOG(DBG,"= computeCollapsedComponentInfos(" << 00885 printrange(comps) << "," << printrange(sharedcomps) << ",.,.,.)"); 00886 00887 // dependencies from the new components to other components 00888 DepMap& incoming = newIncomingDependencies; 00889 // dependencies from other components to the new component 00890 DepMap& outgoing = newOutgoingDependencies; 00891 00892 // set of original components that depend on other original components 00893 // (we need this to find out whether an eatom in a component is an outer or 00894 // an inner eatom ... if it depends on stuff in the components it is inner) 00895 ComponentSet internallyDepends; 00896 00897 // whether within the new component there is a negative rule dependency 00898 bool foundInternalNegativeRuleDependency = false; 00899 00900 // iterate over all originals and over their outgoing dependencies (what they depend on) 00901 ComponentSet::const_iterator ito; 00902 for(ito = comps.begin(); ito != comps.end(); ++ito) { 00903 DBGLOG(DBG,"original " << *ito << ": (outgoing)"); 00904 DBGLOG_INDENT(DBG); 00905 00906 PredecessorIterator itpred, itpred_end; 00907 for(boost::tie(itpred, itpred_end) = getDependencies(*ito); 00908 itpred != itpred_end; ++itpred) { 00909 Dependency outgoing_dep = *itpred; 00910 Component target = targetOf(outgoing_dep); 00911 const DependencyInfo& outgoing_depInfo = propsOf(outgoing_dep); 00912 if( comps.find(target) == comps.end() ) { 00913 // dependency not within the new collapsed component 00914 DBGLOG(DBG,"outgoing dependency to " << target); 00915 outgoing[target] |= outgoing_depInfo; 00916 } 00917 else { 00918 // dependency within the new collapsed component 00919 DBGLOG(DBG,"internal dependency (to " << target << ")"); 00920 internallyDepends.insert(*ito); 00921 00922 if( outgoing_depInfo.negativeRule ) 00923 foundInternalNegativeRuleDependency = true; 00924 } 00925 } // iterate over predecessors 00926 } // iterate over `comps' originals 00927 00928 // iterate over `comps' originals and over incoming dependencies 00929 // now also check for duplicate violations 00930 for(ito = comps.begin(); ito != comps.end(); ++ito) { 00931 DBGLOG(DBG,"original " << *ito << ": (incoming)"); 00932 DBGLOG_INDENT(DBG); 00933 00934 // go over dependencies to original members of new component 00935 SuccessorIterator itsucc, itsucc_end; 00936 for(boost::tie(itsucc, itsucc_end) = getProvides(*ito); 00937 itsucc != itsucc_end; ++itsucc) { 00938 Dependency incoming_dep = *itsucc; 00939 Component source = sourceOf(incoming_dep); 00940 const DependencyInfo& incoming_di = propsOf(incoming_dep); 00941 if( comps.find(source) == comps.end() ) { 00942 // the dependency comes from outside the new component 00943 00944 DBGLOG(DBG,"incoming dependency from " << source); 00945 incoming[source] |= incoming_di; 00946 // ensure that we do not create cycles 00947 // (this check is not too costly, so this is no assertion but a real runtime check) 00948 DepMap::const_iterator itdm = outgoing.find(source); 00949 // if we have an incoming dep and an outgoing dep, 00950 // we create a cycle so this collapsing is invalid 00951 // (this is a bug in the code calling this method!) 00952 if( itdm != outgoing.end() ) { 00953 std::stringstream s; 00954 s << "Error: computeCollapsedComponentInfos tried to create a cycle via component " << source << " and " << *ito; 00955 throw std::runtime_error(s.str()); 00956 } 00957 } 00958 else { 00959 // the dependency comes from inside the new component (to inside) 00960 // nothing to do here (we already did it above 00961 assert(internallyDepends.find(source) != internallyDepends.end() && 00962 "we should have found everything above that we find here"); 00963 } 00964 } // iterate over successors 00965 } // iterate over originals 00966 00967 // 00968 // build newComponentInfo 00969 // 00970 ComponentInfo& ci = newComponentInfo; 00971 if( !sharedcomps.empty() ) 00972 LOG(WARNING,"maybe we need to do more about shared constraint components `sharedcomps' here, at the moment we just copy them!"); 00973 for(ito = sharedcomps.begin(); ito != sharedcomps.end(); ++ito) { 00974 const ComponentInfo& cio = propsOf(*ito); 00975 #ifdef COMPGRAPH_SOURCESDEBUG 00976 ci.sources.insert(ci.sources.end(), 00977 cio.sources.begin(), cio.sources.end()); 00978 #endif 00979 if( !cio.innerRules.empty() || 00980 !cio.innerEatoms.empty() ) 00981 throw std::runtime_error("ccomps must only contain inner constraints!"); 00982 // inner constraints stay inner constraints 00983 ci.innerConstraints.insert(ci.innerConstraints.end(), 00984 cio.innerConstraints.begin(), cio.innerConstraints.end()); 00985 00986 WARNING("do we need to do more here? perhaps negative dependencies to this unit? recursive aggregates?") 00987 } 00988 for(ito = comps.begin(); ito != comps.end(); ++ito) { 00989 const ComponentInfo& cio = propsOf(*ito); 00990 #ifdef COMPGRAPH_SOURCESDEBUG 00991 ci.sources.insert(ci.sources.end(), 00992 cio.sources.begin(), cio.sources.end()); 00993 #endif 00994 // inner rules stay inner rules 00995 ci.innerRules.insert(ci.innerRules.end(), 00996 cio.innerRules.begin(), cio.innerRules.end()); 00997 // inner eatoms always stay inner eatoms, they cannot become outer eatoms 00998 ci.innerEatoms.insert(ci.innerEatoms.end(), 00999 cio.innerEatoms.begin(), cio.innerEatoms.end()); 01000 // inner constraints stay inner constraints 01001 ci.innerConstraints.insert(ci.innerConstraints.end(), 01002 cio.innerConstraints.begin(), cio.innerConstraints.end()); 01003 // information about strongly safe variables and stratified literals 01004 typedef std::pair<ID, std::set<ID> > Pair; 01005 BOOST_FOREACH (Pair p, cio.stronglySafeVariables) { 01006 BOOST_FOREACH (ID id, p.second) { 01007 ci.stronglySafeVariables[p.first].insert(id); 01008 } 01009 } 01010 ci.predicatesDefinedInComponent.insert( 01011 cio.predicatesDefinedInComponent.begin(), cio.predicatesDefinedInComponent.end()); 01012 ci.predicatesOccurringInComponent.insert( 01013 cio.predicatesOccurringInComponent.begin(), cio.predicatesOccurringInComponent.end()); 01014 /* 01015 BOOST_FOREACH (Pair p, cio.stratifiedLiterals){ 01016 BOOST_FOREACH (ID id, p.second){ 01017 ci.stratifiedLiterals[p.first].insert(id); 01018 } 01019 } 01020 */ 01021 01022 ci.disjunctiveHeads |= cio.disjunctiveHeads; 01023 ci.negativeDependencyBetweenRules |= cio.negativeDependencyBetweenRules; 01024 01025 ci.innerEatomsNonmonotonic |= cio.innerEatomsNonmonotonic; 01026 ci.componentIsMonotonic |= cio.componentIsMonotonic; 01027 01028 WARNING("check if fixedDomain is propagated correctly, it is missing in operator|= but this might be on purpose") 01029 // fixedDomain: 01030 // pure external components shall have no influence on this property 01031 // because domain restriction is always done in successor components 01032 if (!(!cio.outerEatoms.empty() && cio.innerRules.empty())) 01033 ci.fixedDomain &= cio.fixedDomain; 01034 01035 // outer external atoms which get input from the same component become inner ones 01036 BOOST_FOREACH (ID outerEA, cio.outerEatoms){ 01037 const ExternalAtom& eatom = reg->eatoms.getByID(outerEA); 01038 bool becomesInner = false; 01039 becomesInner = (ci.predicatesDefinedInComponent.find(eatom.auxInputPredicate) != ci.predicatesDefinedInComponent.end()); 01040 for (int i = 0; i < eatom.inputs.size(); ++i) { 01041 if (eatom.pluginAtom->getInputType(i) == PluginAtom::PREDICATE && ci.predicatesDefinedInComponent.find(eatom.inputs[i]) != ci.predicatesDefinedInComponent.end()){ 01042 becomesInner = true; 01043 break; 01044 } 01045 } 01046 01047 if (!becomesInner) { 01048 ci.outerEatoms.insert(ci.outerEatoms.end(), outerEA); 01049 ci.outerEatomsNonmonotonic |= eatom.getExtSourceProperties().isNonmonotonic(); 01050 }else{ 01051 ci.innerEatoms.insert(ci.innerEatoms.end(), outerEA); 01052 ci.innerEatomsNonmonotonic |= eatom.getExtSourceProperties().isNonmonotonic(); 01053 } 01054 } 01055 WARNING("if " input " component consists only of eatoms, they may be nonmonotonic, and we still can have wellfounded model generator ... create testcase for this ? how about wellfounded2.hex?") 01056 } 01057 ci.negativeDependencyBetweenRules |= foundInternalNegativeRuleDependency; 01058 // recompute if the collapsed component contains recursive aggregates; note that this is not simply the logical or of the basic components 01059 ci.recursiveAggregates = computeRecursiveAggregatesInComponent(ci); 01060 calculateStratificationInfo(reg, ci); 01061 } 01062 01063 01064 // collapse components given in range into one new component 01065 // originals are put into new component and then removed 01066 // shared are just copied into new component 01067 // collapse incoming and outgoing dependencies 01068 // update properties of dependencies 01069 // update properties of component 01070 ComponentGraph::Component 01071 ComponentGraph::collapseComponents( 01072 const ComponentSet& originals, const ComponentSet& shared) 01073 { 01074 DBGLOG_SCOPE(DBG,"cC", false); 01075 DBGLOG(DBG,"= collapseComponents(" << printrange(originals) << "," << printrange(shared) << ")"); 01076 01077 Component c = boost::add_vertex(cg); 01078 LOG(DBG,"created component node " << c << " for collapsed component"); 01079 01080 DepMap incoming; 01081 DepMap outgoing; 01082 ComponentInfo& ci = propsOf(c); 01083 computeCollapsedComponentInfos(originals, shared, incoming, outgoing, ci); 01084 01085 // build incoming dependencies 01086 for(DepMap::const_iterator itd = incoming.begin(); 01087 itd != incoming.end(); ++itd) { 01088 Dependency newdep; 01089 bool success; 01090 DBGLOG(DBG,"adding incoming edge " << itd->first << " -> " << c); 01091 boost::tie(newdep, success) = boost::add_edge(itd->first, c, itd->second, cg); 01092 assert(success); // we only add new edges here, and each only once 01093 } 01094 01095 // build outgoing dependencies 01096 for(DepMap::const_iterator itd = outgoing.begin(); 01097 itd != outgoing.end(); ++itd) { 01098 Dependency newdep; 01099 bool success; 01100 DBGLOG(DBG,"adding outgoing edge " << c << " -> " << itd->first); 01101 boost::tie(newdep, success) = boost::add_edge(c, itd->first, itd->second, cg); 01102 assert(success); // we only add new edges here, and each only once 01103 } 01104 01105 // remove all original components 01106 for(ComponentSet::const_iterator ito = originals.begin(); 01107 ito != originals.end(); ++ito) { 01108 boost::clear_vertex(*ito, cg); 01109 boost::remove_vertex(*ito, cg); 01110 } 01111 01112 return c; 01113 } 01114 01115 01116 namespace 01117 { 01118 std::string graphviz_node_id(ComponentGraph::Component c) { 01119 std::ostringstream os; 01120 os << "c" << std::hex << c; 01121 return os.str(); 01122 } 01123 01124 template<typename Range> 01125 void printoutVerboseIfNotEmpty(std::ostream& o, RegistryPtr reg, const char* prefix, Range idrange) { 01126 // see boost/range/iterator_range.hpp 01127 typedef typename Range::const_iterator Iterator; 01128 if( !boost::empty(idrange) ) { 01129 o << "{" << prefix << "|"; 01130 graphviz::escape(o, printManyToString<RawPrinter>(idrange, "\n", reg)); 01131 o << "}|"; 01132 } 01133 } 01134 01135 template<typename Range> 01136 void printoutTerseIfNotEmpty(std::ostream& o, RegistryPtr reg, const char* prefix, Range idrange) { 01137 // see boost/range/iterator_range.hpp 01138 typedef typename Range::const_iterator Iterator; 01139 if( !boost::empty(idrange) ) { 01140 unsigned count = 0; 01141 for(Iterator it = boost::begin(idrange); it != boost::end(idrange); ++it, ++count) 01142 ; 01143 o << "{" << prefix << ":" << count << "}|"; 01144 } 01145 } 01146 } 01147 01148 01149 void ComponentGraph::writeGraphVizComponentLabel(std::ostream& o, Component c, unsigned index, bool verbose) const 01150 { 01151 const ComponentInfo& ci = getComponentInfo(c); 01152 if( verbose ) { 01153 o << "{idx=" << index << ",component=" << c << "|"; 01154 #ifdef COMPGRAPH_SOURCESDEBUG 01155 o << "{sources|" << printrange(ci.sources, "\\{", ",", "\\}") << "}|"; 01156 #endif 01157 printoutVerboseIfNotEmpty(o, reg, "outerEatoms", ci.outerEatoms); 01158 printoutVerboseIfNotEmpty(o, reg, "innerRules", ci.innerRules); 01159 printoutVerboseIfNotEmpty(o, reg, "innerEatoms", ci.innerEatoms); 01160 printoutVerboseIfNotEmpty(o, reg, "innerConstraints", ci.innerConstraints); 01161 if( !ci.innerRules.empty() ) { 01162 if( ci.disjunctiveHeads ) 01163 o << "{rules contain disjunctive heads}|"; 01164 if( ci.negativeDependencyBetweenRules ) 01165 o << "{rules contain negation in cycles}|"; 01166 } 01167 if( !ci.innerEatoms.empty() && ci.innerEatomsNonmonotonic ) 01168 o << "{inner eatoms nonmonotonic}|"; 01169 if( !ci.outerEatoms.empty() && ci.outerEatomsNonmonotonic ) 01170 o << "{outer eatoms nonmonotonic}|"; 01171 01172 if( ci.fixedDomain ) 01173 o << "{fixed domain}|"; 01174 if( ci.recursiveAggregates ) 01175 o << "{recursive aggregates}|"; 01176 o << "}"; 01177 } 01178 else { 01179 o << "{idx=" << index << "|"; 01180 printoutTerseIfNotEmpty(o, reg, "outerEatoms", ci.outerEatoms); 01181 printoutTerseIfNotEmpty(o, reg, "innerRules", ci.innerRules); 01182 printoutTerseIfNotEmpty(o, reg, "innerEatoms", ci.innerEatoms); 01183 printoutTerseIfNotEmpty(o, reg, "innerConstraints", ci.innerConstraints); 01184 if( !ci.innerRules.empty() ) { 01185 if( ci.disjunctiveHeads ) 01186 o << "{rules contain disjunctive heads}|"; 01187 if( ci.negativeDependencyBetweenRules ) 01188 o << "{rules contain negation in cycles}|"; 01189 } 01190 if( !ci.innerEatoms.empty() || ci.innerEatomsNonmonotonic ) 01191 o << "{inner eatoms nonmonotonic}|"; 01192 if( !ci.outerEatoms.empty() || ci.outerEatomsNonmonotonic ) 01193 o << "{outer eatoms nonmonotonic}|"; 01194 o << "}"; 01195 } 01196 } 01197 01198 01199 void ComponentGraph::writeGraphVizDependencyLabel(std::ostream& o, Dependency dep, bool verbose) const 01200 { 01201 const DependencyInfo& di = getDependencyInfo(dep); 01202 if( verbose ) { 01203 o << di; 01204 } 01205 else { 01206 o << 01207 (di.positiveRegularRule?" posR":"") << 01208 (di.positiveConstraint?" posC":"") << 01209 (di.negativeRule?" negR":"") << 01210 (di.unifyingHead?" unifying":"") << 01211 (di.positiveExternal?" posExt":"") << 01212 (di.negativeExternal?" negExt":"") << 01213 (di.externalConstantInput?" extConstInp":"") << 01214 (di.externalPredicateInput?" extPredInp":"") << 01215 (di.externalNonmonotonicPredicateInput?" extNonmonPredInp":""); 01216 } 01217 } 01218 01219 01220 // output graph as graphviz source 01221 void ComponentGraph::writeGraphViz(std::ostream& o, bool verbose) const 01222 { 01223 // boost::graph::graphviz is horribly broken! 01224 // therefore we print it ourselves 01225 01226 o << "digraph G {" << std::endl << 01227 "rankdir=BT;" << std::endl;// print root nodes at bottom, leaves at top! 01228 01229 // print vertices 01230 ComponentIterator it, it_end; 01231 unsigned index = 0; 01232 for(boost::tie(it, it_end) = boost::vertices(cg); 01233 it != it_end; ++it, ++index) { 01234 o << graphviz_node_id(*it) << "[shape=record,label=\""; 01235 { 01236 writeGraphVizComponentLabel(o, *it, index, verbose); 01237 } 01238 o << "\"];" << std::endl; 01239 } 01240 01241 // print edges 01242 DependencyIterator dit, dit_end; 01243 for(boost::tie(dit, dit_end) = boost::edges(cg); 01244 dit != dit_end; ++dit) { 01245 Component src = sourceOf(*dit); 01246 Component target = targetOf(*dit); 01247 o << graphviz_node_id(src) << " -> " << graphviz_node_id(target) << 01248 "[label=\""; 01249 { 01250 writeGraphVizDependencyLabel(o, *dit, verbose); 01251 } 01252 o << "\"];" << std::endl; 01253 } 01254 01255 o << "}" << std::endl; 01256 } 01257 01258 01259 ComponentGraph::ComponentGraph(const ComponentGraph& other): 01260 ctx(other.ctx), 01261 reg(other.reg), 01262 #ifdef COMPGRAPH_SOURCESDEBUG 01263 dg(other.dg), 01264 #endif 01265 cg(other.cg) 01266 { 01267 DBGLOG(DBG, "Building component graph"); 01268 } 01269 01270 01271 // for explicit cloning of the graph 01272 ComponentGraph* ComponentGraph::clone() const 01273 { 01274 return new ComponentGraph(*this); 01275 } 01276 01277 01278 DLVHEX_NAMESPACE_END 01279 01280 01281 // vim:expandtab:ts=4:sw=4: 01282 // mode: C++ 01283 // End: