dlvhex  2.5.0
src/EvalHeuristicGreedy.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 
00035 #ifdef HAVE_CONFIG_H
00036 #include "config.h"
00037 #endif                           // HAVE_CONFIG_H
00038 
00039 #include "dlvhex2/EvalHeuristicGreedy.h"
00040 #include "dlvhex2/EvalHeuristicShared.h"
00041 #include "dlvhex2/Logger.h"
00042 #include "dlvhex2/ProgramCtx.h"
00043 #include "dlvhex2/LiberalSafetyChecker.h"
00044 
00045 #include <boost/unordered_map.hpp>
00046 #include <boost/property_map/property_map.hpp>
00047 #include <boost/graph/breadth_first_search.hpp>
00048 #include <boost/graph/visitors.hpp>
00049 #include <boost/graph/depth_first_search.hpp>
00050 #include <boost/graph/properties.hpp>
00051 #include <boost/scoped_ptr.hpp>
00052 
00053 //#include <fstream>
00054 
00055 DLVHEX_NAMESPACE_BEGIN
00056 
00057 bool EvalHeuristicGreedy::mergeComponents(ProgramCtx& ctx, const ComponentGraph::ComponentInfo& ci1, const ComponentGraph::ComponentInfo& ci2, bool negativeExternalDependency) const
00058 {
00059 
00060     if (ctx.config.getOption("LiberalSafety") && ctx.config.getOption("IncludeAuxInputInAuxiliaries")) {
00061         // here we could always merge
00062         // however, we do this only if there are no negative external dependencies between the components (as this comes at exponential cost)
00063         return !negativeExternalDependency;
00064     }
00065     else {
00066 
00067         // never merge components with outer external atoms (they could become inner ones)
00068         if (!ci1.outerEatoms.empty() || !ci2.outerEatoms.empty()) return false;
00069 
00070         // if both components have a fixed domain we can safely merge them
00071         // (both can be solved by guess&check mg)
00072         if (ci1.fixedDomain && ci2.fixedDomain) return true;
00073 
00074         // if both components are solved by wellfounded mg and none of them has outer external atoms, then we merge them
00075         // (the resulting component will still be wellfounded and an outer external atom can not become an inner one)
00076         if (!ci1.innerEatomsNonmonotonic && !ci1.negativeDependencyBetweenRules && !ci1.disjunctiveHeads && ci1.innerEatoms.size() > 0 && ci1.outerEatoms.size() == 0 &&
00077             !ci2.innerEatomsNonmonotonic && !ci2.negativeDependencyBetweenRules && !ci2.disjunctiveHeads && ci2.innerEatoms.size() > 0 && ci2.outerEatoms.size() == 0) return true;
00078 
00079         // otherwise: don't merge them
00080         return false;
00081     }
00082 }
00083 
00084 
00085 EvalHeuristicGreedy::EvalHeuristicGreedy():
00086 Base()
00087 {
00088 }
00089 
00090 
00091 EvalHeuristicGreedy::~EvalHeuristicGreedy()
00092 {
00093 }
00094 
00095 
00096 typedef ComponentGraph::Component Component;
00097 typedef ComponentGraph::ComponentIterator ComponentIterator;
00098 typedef std::vector<Component> ComponentContainer;
00099 typedef ComponentGraph::ComponentSet ComponentSet;
00100 
00101 namespace internalgreedy
00102 {
00103 
00104     // collect all components on the way
00105     struct DFSVisitor:
00106     public boost::default_dfs_visitor
00107     {
00108         const ComponentGraph& cg;
00109         ComponentSet& comps;
00110         DFSVisitor(const ComponentGraph& cg, ComponentSet& comps): boost::default_dfs_visitor(), cg(cg), comps(comps) {}
00111         DFSVisitor(const DFSVisitor& other): boost::default_dfs_visitor(), cg(other.cg), comps(other.comps) {}
00112         template<typename GraphT>
00113         void discover_vertex(Component comp, const GraphT&) {
00114             comps.insert(comp);
00115         }
00116     };
00117 
00118     template<typename ComponentGraph, typename Set>
00119     void transitivePredecessorComponents(const ComponentGraph& compgraph, Component from, Set& preds) {
00120         // we need a hash map, as component graph is no graph with vecS-storage
00121         //
00122         typedef boost::unordered_map<Component, boost::default_color_type> CompColorHashMap;
00123         typedef boost::associative_property_map<CompColorHashMap> CompColorMap;
00124         CompColorHashMap ccWhiteHashMap;
00125         // fill white hash map
00126         ComponentIterator cit, cit_end;
00127         for(boost::tie(cit, cit_end) = compgraph.getComponents();
00128         cit != cit_end; ++cit) {
00129             //boost::put(ccWhiteHashMap, *cit, boost::white_color);
00130             ccWhiteHashMap[*cit] = boost::white_color;
00131         }
00132         CompColorHashMap ccHashMap(ccWhiteHashMap);
00133 
00134         //
00135         // do DFS
00136         //
00137         DFSVisitor dfs_vis(compgraph, preds);
00138         //LOG("doing dfs visit for root " << *itr);
00139         boost::depth_first_visit(
00140             compgraph.getInternalGraph(),
00141             from,
00142             dfs_vis,
00143             CompColorMap(ccHashMap));
00144         DBGLOG(DBG,"predecessors of " << from << " are " << printrange(preds));
00145     }
00146 
00147 }
00148 
00149 
00150 // required for some GCCs for DFSVisitor CopyConstructible Concept Check
00151 using namespace internalgreedy;
00152 
00153 void EvalHeuristicGreedy::build(EvalGraphBuilder& builder)
00154 {
00155     ProgramCtx& ctx = builder.getProgramCtx();
00156     ComponentGraph& compgraph = builder.getComponentGraph();
00157     #if 0
00158     {
00159         std::string fnamev = "my_initial_ClonedCompGraphVerbose.dot";
00160         std::ofstream filev(fnamev.c_str());
00161         compgraph.writeGraphViz(filev, true);
00162     }
00163     #endif
00164 
00165     bool didSomething;
00166     do {
00167         didSomething = false;
00168 
00169         //
00170         // forall external components e:
00171         // merge with all rules that
00172         // * depend on e
00173         // * do not contain external atoms
00174         // * do not depend on something e does not (transitively) depend on
00175         //
00176         {
00177             ComponentIterator cit;
00178                                  // do not use boost::tie here! the container is modified in the loop!
00179             for(cit = compgraph.getComponents().first;
00180             cit != compgraph.getComponents().second; ++cit) {
00181                 Component comp = *cit;
00182                                  // || !compgraph.propsOf(comp).innerRules.empty() || !compgraph.propsOf(comp).innerConstraints.empty() )
00183                 if( compgraph.propsOf(comp).outerEatoms.empty() )
00184                     continue;
00185                 DBGLOG(DBG,"checking component " << comp);
00186 
00187                 LOG(ANALYZE,"checking whether to collapse external component " << comp << " with successors");
00188 
00189                 // get predecessors
00190                 ComponentSet preds;
00191                 transitivePredecessorComponents(compgraph, comp, preds);
00192 
00193                 // get successors
00194                 ComponentSet collapse;
00195                 bool addedToCollapse;
00196                 // do this as long as we find new ones
00197                 // if we do not do this loop, we might miss something
00198                 // as PredecessorIterator not necessarily honours topological order
00199                 // (TODO this could be made more efficient)
00200                 do {
00201                     addedToCollapse = false;
00202 
00203                     ComponentGraph::SuccessorIterator sit, sit_end;
00204                     for(boost::tie(sit, sit_end) = compgraph.getProvides(comp);
00205                     sit != sit_end; ++sit) {
00206                         Component succ = compgraph.sourceOf(*sit);
00207 
00208                         // skip successors with eatoms
00209                         if( !compgraph.propsOf(succ).outerEatoms.empty() )
00210                             continue;
00211                         // do not check found stuff twice
00212                         if( collapse.find(succ) != collapse.end() )
00213                             continue;
00214 
00215                         DBGLOG(DBG,"found successor " << succ);
00216 
00217                         ComponentGraph::PredecessorIterator pit, pit_end;
00218                         bool good = true;
00219                         for(boost::tie(pit, pit_end) = compgraph.getDependencies(succ);
00220                         pit != pit_end; ++pit) {
00221                             Component dependson = compgraph.targetOf(*pit);
00222                             if( preds.find(dependson) == preds.end() ) {
00223                                 LOG(DBG,"successor bad as it depends on other node " << dependson);
00224                                 good = false;
00225                                 break;
00226                             }
00227                         }
00228                         if( good ) {
00229                             // collapse with this
00230                             collapse.insert(succ);
00231                             preds.insert(succ);
00232                             addedToCollapse = true;
00233                         }
00234                     }
00235                 }
00236                 while(addedToCollapse);
00237 
00238                 // collapse if not nonempty
00239                 if( !collapse.empty() ) {
00240                     collapse.insert(comp);
00241                     Component c = compgraph.collapseComponents(collapse);
00242                     LOG(ANALYZE,"collapse of " << printrange(collapse) << " yielded new component " << c);
00243 
00244                     // restart loop after collapse
00245                     cit = compgraph.getComponents().first;
00246                     didSomething = true;
00247                 }
00248             }
00249         }
00250 
00251         //
00252         // forall components c1:
00253         // merge with all other components c2 such that no cycle is broken
00254         // that is, there must not be a path of length >=2 from c2 to c1
00255         //
00256         {
00257             ComponentIterator cit = compgraph.getComponents().first;
00258             while(cit != compgraph.getComponents().second) {
00259                 Component comp = *cit;
00260                 ComponentSet collapse;
00261                 DBGLOG(DBG,"checking component " << comp);
00262 
00263                 ComponentIterator cit2 =  cit;
00264                 cit2++;
00265                 while( cit2 != compgraph.getComponents().second ) {
00266                     Component comp2 = *cit2;
00267                     DBGLOG(DBG,"checking other component " << comp2);
00268 
00269                     bool breakCycle = false;
00270 
00271                     // check if there is a path of length >=2 from comp2 to comp
00272                     // that is, there is a path from a predecessor of comp2 to comp
00273                     ComponentSet preds2;
00274                     {
00275                         ComponentGraph::PredecessorIterator pit, pit_end;
00276                         for(boost::tie(pit, pit_end) = compgraph.getDependencies(comp2);
00277                         pit != pit_end; ++pit) {
00278                             preds2.insert(compgraph.targetOf(*pit));
00279                         }
00280                     }
00281                     BOOST_FOREACH (Component comp2s, preds2) {
00282 
00283                         ComponentSet reachable;
00284                         transitivePredecessorComponents(compgraph, comp2s, reachable);
00285 
00286                                  // path of length >=2
00287                         if (std::find(reachable.begin(), reachable.end(), comp) != reachable.end() && comp2s != comp) {
00288                             DBGLOG(DBG, "do not merge because this would break a cycle");
00289                             breakCycle = true;
00290                             break;
00291                         }
00292                     }
00293 
00294                     // check if there is a path of length >=2 from comp to comp2
00295                     // that is, there is a path from a predecessor of comp to comp2
00296                     ComponentSet preds;
00297                     {
00298                         ComponentGraph::PredecessorIterator pit, pit_end;
00299                         for(boost::tie(pit, pit_end) = compgraph.getDependencies(comp);
00300                         pit != pit_end; ++pit) {
00301                             preds.insert(compgraph.targetOf(*pit));
00302                         }
00303                     }
00304                     BOOST_FOREACH (Component comps, preds) {
00305 
00306                         ComponentSet reachable;
00307                         transitivePredecessorComponents(compgraph, comps, reachable);
00308 
00309                                  // path of length >=2
00310                         if (std::find(reachable.begin(), reachable.end(), comp2) != reachable.end() && comps != comp2) {
00311                             DBGLOG(DBG, "do not merge because this would break a cycle");
00312                             breakCycle = true;
00313                             break;
00314                         }
00315                     }
00316 
00317                     std::set<std::pair<ComponentGraph::Component, ComponentGraph::Component> > negdep;
00318                     std::set<ComponentGraph::Component> nonmonotonicPredecessor;
00319                     if (ctx.config.getOption("LiberalSafety") && ctx.config.getOption("IncludeAuxInputInAuxiliaries")) {
00320                         if (ctx.config.getOption("LiberalSafety") && ctx.config.getOption("IncludeAuxInputInAuxiliaries")) {
00321                             // check if there is a nonmonotonic external dependency from comp to comp2
00322                             BOOST_FOREACH (ComponentGraph::Dependency dep, compgraph.getDependencies()) {
00323                                 const ComponentGraph::DependencyInfo& di = compgraph.getDependencyInfo(dep);
00324                                 if (di.externalNonmonotonicPredicateInput) {
00325                                     // check if the nonmonotonic predicate dependency is eliminated if we consider only necessary external atoms
00326                                     BOOST_FOREACH (ComponentGraph::DependencyInfo::DepEdge de, di.depEdges) {
00327                                         if (de.get<2>().externalNonmonotonicPredicateInput &&
00328                                         ctx.liberalSafetyChecker->isExternalAtomNecessaryForDomainExpansionSafety(de.get<0>())) {
00329                                             // not eliminated
00330                                             negdep.insert(std::pair<ComponentGraph::Component, ComponentGraph::Component>(compgraph.sourceOf(dep), compgraph.targetOf(dep)));
00331                                             nonmonotonicPredecessor.insert(compgraph.sourceOf(dep));
00332                                             break;
00333                                         }
00334                                     }
00335                                 }
00336                             }
00337                         }
00338                     }
00339 
00340                     // if this is the case, then do not merge
00341                     if (!breakCycle) {
00342                         // we do not want to merge if a component in transitivePredecessorComponents is reachable from exactly one of comp and comp2
00343                         bool nd = false;
00344                         if (ctx.config.getOption("LiberalSafety") && ctx.config.getOption("IncludeAuxInputInAuxiliaries")) {
00345                             // never merge C1 and C2 if C1 has a nonmonotonic predecessor unit but C2 has not
00346                             // Example:
00347                             //     C4
00348                             //    /  \-
00349                             //   C3  C2
00350                             //    \  /
00351                             //     C1
00352                             // Here, C1 is not merged with C3, which is by intend: merging them would prevent the merging of C3 with C4
00353                             ComponentSet reachable1, reachable2;
00354                             transitivePredecessorComponents(compgraph, comp, reachable1);
00355                             transitivePredecessorComponents(compgraph, comp2, reachable2);
00356                             bool nonmonTrans1 = false;
00357                             bool nonmonTrans2 = false;
00358                             BOOST_FOREACH (Component c, reachable1) if (nonmonotonicPredecessor.find(c) != nonmonotonicPredecessor.end()) nonmonTrans1 = true;
00359                             BOOST_FOREACH (Component c, reachable2) if (nonmonotonicPredecessor.find(c) != nonmonotonicPredecessor.end()) nonmonTrans2 = true;
00360                             nd |= nonmonTrans1 != nonmonTrans2;
00361 
00362                             // never merge if one of the components has a nonmonotonic dependency to some predecessor
00363                             // (the dependency could become internal, which slows down grounding significantly)
00364                             //nd |= (nonmonotonicPredecessor.find(comp) != nonmonotonicPredecessor.end()) ||
00365                             //    (nonmonotonicPredecessor.find(comp2) != nonmonotonicPredecessor.end());
00366 
00367                             // never merge if this makes a nonmonotonic dependency internal
00368                             // (should be a more specific version of the commented check above)
00369                             nd |= (negdep.find(std::pair<ComponentGraph::Component, ComponentGraph::Component>(comp, comp2)) != negdep.end()) ||
00370                                   (negdep.find(std::pair<ComponentGraph::Component, ComponentGraph::Component>(comp2, comp)) != negdep.end());
00371                         }
00372 
00373                         if (mergeComponents(ctx, compgraph.propsOf(comp), compgraph.propsOf(comp2), nd)) {
00374                             if (std::find(collapse.begin(), collapse.end(), comp2) == collapse.end()) {
00375                                 collapse.insert(comp2);
00376                                 // merge only one pair at a time, otherwise this could create cycles which are not detected above:
00377                                 // e.g. C1     C2 --> C3 --> C4
00378                                 // C1 can be merged with C2 and C1 can be merged with C4, but it can't be merged with both of them because this would create a cycle
00379                                 // This is only detected if we see {C1, C2} (or {C1, C4}) as intermediate result
00380                                 break;
00381                             }
00382                         }
00383                     }
00384 
00385                     cit2++;
00386                 }
00387 
00388                 if( !collapse.empty() ) {
00389                     // collapse! (decreases graph size)
00390                     collapse.insert(comp);
00391                     assert(collapse.size() > 1);
00392                     Component c = compgraph.collapseComponents(collapse);
00393                     LOG(ANALYZE,"collapse of " << printrange(collapse) << " yielded new component " << c);
00394 
00395                     // restart loop after collapse
00396                     cit = compgraph.getComponents().first;
00397                     didSomething = true;
00398                 }
00399                 else {
00400                     // advance
00401                     ++cit;
00402                 }
00403             }
00404         }
00405 
00406     }
00407     while(didSomething);
00408 
00409     //
00410     // create eval units using topological sort
00411     //
00412     ComponentContainer sortedcomps;
00413     evalheur::topologicalSortComponents(compgraph.getInternalGraph(), sortedcomps);
00414     LOG(ANALYZE,"now creating evaluation units from components " << printrange(sortedcomps));
00415     #if 0
00416     {
00417         std::string fnamev = "my_ClonedCompGraphVerbose.dot";
00418         std::ofstream filev(fnamev.c_str());
00419         compgraph.writeGraphViz(filev, true);
00420     }
00421     #endif
00422     for(ComponentContainer::const_iterator it = sortedcomps.begin();
00423     it != sortedcomps.end(); ++it) {
00424         // just create a unit from each component (we collapsed above)
00425         std::list<Component> comps;
00426         comps.push_back(*it);
00427         std::list<Component> ccomps;
00428         EvalGraphBuilder::EvalUnit u = builder.createEvalUnit(comps, ccomps);
00429         LOG(ANALYZE,"component " << *it << " became eval unit " << u);
00430     }
00431 }
00432 
00433 
00434 DLVHEX_NAMESPACE_END
00435 
00436 
00437 // vim:expandtab:ts=4:sw=4:
00438 // mode: C++
00439 // End: