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 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: