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