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/LiberalSafetyChecker.h" 00040 #include "dlvhex2/Logger.h" 00041 #include "dlvhex2/Registry.h" 00042 #include "dlvhex2/ProgramCtx.h" 00043 #include "dlvhex2/SafetyChecker.h" 00044 #include "dlvhex2/Printer.h" 00045 #include "dlvhex2/Rule.h" 00046 #include "dlvhex2/Atoms.h" 00047 #include "dlvhex2/PluginInterface.h" 00048 #include "dlvhex2/GraphvizHelpers.h" 00049 00050 #include <boost/property_map/property_map.hpp> 00051 #include <boost/foreach.hpp> 00052 #include <boost/algorithm/string/replace.hpp> 00053 #include <boost/range/join.hpp> 00054 #include <boost/graph/breadth_first_search.hpp> 00055 #include <boost/graph/visitors.hpp> 00056 #include <boost/graph/strong_components.hpp> 00057 00058 #include <sstream> 00059 00060 DLVHEX_NAMESPACE_BEGIN 00061 00062 namespace 00063 { 00064 00065 // Exploits semantic annotation "finiteness" of external atoms to ensure safety 00066 class FinitenessChecker : public LiberalSafetyPlugin 00067 { 00068 private: 00069 bool dorun; 00070 public: 00071 FinitenessChecker(LiberalSafetyChecker& lsc) : LiberalSafetyPlugin(lsc) { 00072 dorun = true; 00073 } 00074 00075 void run() { 00076 if (!dorun) return; 00077 dorun = false; 00078 00079 // make output variables of external atoms bounded, if they are in a position with finite domain 00080 BOOST_FOREACH (ID ruleID, lsc.getIdb()) { 00081 const Rule& rule = lsc.reg->rules.getByID(ruleID); 00082 BOOST_FOREACH (ID b, rule.body) { 00083 if (b.isNaf()) continue; 00084 00085 if (b.isExternalAtom()) { 00086 const ExternalAtom& eatom = lsc.reg->eatoms.getByID(b); 00087 00088 // finite domain 00089 for (uint32_t i = 0; i < eatom.tuple.size(); ++i) { 00090 if (eatom.getExtSourceProperties().hasFiniteDomain(i)) { 00091 LiberalSafetyChecker::VariableLocation vl(ruleID, eatom.tuple[i]); 00092 if (lsc.getBoundedVariables().count(vl) == 0) { 00093 DBGLOG(DBG, "Variable " << vl.first.address << "/" << vl.second.address << " is bounded because output element " << i << " of external atom " << b << " has a finite domain"); 00094 lsc.addExternallyBoundedVariable(b, vl); 00095 } 00096 } 00097 } 00098 00099 // relative finite domain 00100 typedef std::pair<int, int> RelativeFinitePair; 00101 BOOST_FOREACH (RelativeFinitePair rfp, eatom.getExtSourceProperties().relativeFiniteOutputDomain) { 00102 dorun = true; 00103 // check if the respective input parameter is safe in all attributes 00104 bool applies = false; 00105 if (eatom.pluginAtom->getInputType(rfp.first) == PluginAtom::CONSTANT) { 00106 LiberalSafetyChecker::VariableLocation vl(ruleID, eatom.inputs[rfp.second]); 00107 applies = lsc.getBoundedVariables().count(vl) > 0; 00108 } 00109 else { 00110 applies = true; 00111 for (int k = 0; k < lsc.getPredicateArity(eatom.inputs[rfp.second]); k++) { 00112 if (lsc.getDomainExpansionSafeAttributes().count(lsc.getAttribute(eatom.inputs[rfp.second], k)) == 0) { 00113 applies = false; 00114 break; 00115 } 00116 } 00117 } 00118 00119 // if yes, then the output is safe as well 00120 if (applies) { 00121 LiberalSafetyChecker::VariableLocation vl(ruleID, eatom.tuple[rfp.first]); 00122 if (lsc.getBoundedVariables().count(vl) == 0) { 00123 DBGLOG(DBG, "Variable " << vl.first.address << "/" << vl.second.address << " is bounded because output element " << rfp.first << " of external atom " << b << " has a relative finite domain wrt. safe " << rfp.second); 00124 lsc.addExternallyBoundedVariable(b, vl); 00125 } 00126 } 00127 } 00128 } 00129 } 00130 } 00131 } 00132 }; 00133 00134 // Exploits semantic annotation "finite fiber" of external atoms to ensure safety 00135 class FiniteFiberChecker : public LiberalSafetyPlugin 00136 { 00137 private: 00138 bool firstRun; 00139 public: 00140 FiniteFiberChecker(LiberalSafetyChecker& lsc) : LiberalSafetyPlugin(lsc) { 00141 firstRun = true; 00142 } 00143 00144 void run() { 00145 if (!firstRun) return; 00146 firstRun = false; 00147 00148 // make input variables of external atoms with finite fiber bounded, if all output variables are bounded 00149 BOOST_FOREACH (ID ruleID, lsc.getIdb()) { 00150 const Rule& rule = lsc.reg->rules.getByID(ruleID); 00151 BOOST_FOREACH (ID b, rule.body) { 00152 if (b.isNaf()) continue; 00153 00154 if (b.isExternalAtom()) { 00155 const ExternalAtom& eatom = lsc.reg->eatoms.getByID(b); 00156 00157 bool outputBounded = true; 00158 BOOST_FOREACH (ID var, lsc.reg->getVariablesInTuple(eatom.tuple)) { 00159 if (!(lsc.getBoundedVariables().count(LiberalSafetyChecker::VariableLocation(ruleID, var)) > 0)) { 00160 outputBounded = false; 00161 break; 00162 } 00163 } 00164 if (eatom.getExtSourceProperties().hasFiniteFiber() && outputBounded) { 00165 BOOST_FOREACH (ID var, lsc.reg->getVariablesInTuple(eatom.inputs)) { 00166 LiberalSafetyChecker::VariableLocation vl(ruleID, var); 00167 if (lsc.getBoundedVariables().count(vl) == 0) { 00168 DBGLOG(DBG, "Variable " << "r" << vl.first.address << "/" << vl.second.address << " is bounded because " << b << " has a finite fiber"); 00169 lsc.addExternallyBoundedVariable(b, vl); 00170 } 00171 } 00172 } 00173 } 00174 00175 else if (b.isAggregateAtom()) { 00176 const AggregateAtom& aatom = lsc.reg->aatoms.getByID(b); 00177 if (aatom.tuple[1].address == ID::TERM_BUILTIN_EQ) lsc.addBoundedVariable(LiberalSafetyChecker::VariableLocation(ruleID, aatom.tuple[0])); 00178 if (aatom.tuple[3].address == ID::TERM_BUILTIN_EQ) lsc.addBoundedVariable(LiberalSafetyChecker::VariableLocation(ruleID, aatom.tuple[4])); 00179 } 00180 00181 else if (b.isBuiltinAtom()) { 00182 const BuiltinAtom& batom = lsc.reg->batoms.getByID(b); 00183 if (batom.tuple[0].address == ID::TERM_BUILTIN_INT && batom.tuple[1].isVariableTerm()) lsc.addBoundedVariable(LiberalSafetyChecker::VariableLocation(ruleID, batom.tuple[1])); 00184 } 00185 } 00186 } 00187 } 00188 }; 00189 00190 // Aggregates and builtins to ensure safety 00191 class AggregateAndBuildinChecker : public LiberalSafetyPlugin 00192 { 00193 private: 00194 bool firstRun; 00195 public: 00196 AggregateAndBuildinChecker(LiberalSafetyChecker& lsc) : LiberalSafetyPlugin(lsc) { 00197 firstRun = true; 00198 } 00199 00200 void run() { 00201 if (!firstRun) return; 00202 firstRun = false; 00203 00204 // 1. make variables bounded, which are assigned to an aggregate (because then #maxint ensures that there are only finitly many differnt values) 00205 // 2. make variables in #int(...) atoms bounded 00206 BOOST_FOREACH (ID ruleID, lsc.getIdb()) { 00207 const Rule& rule = lsc.reg->rules.getByID(ruleID); 00208 BOOST_FOREACH (ID b, rule.body) { 00209 if (b.isNaf()) continue; 00210 00211 // 1 00212 if (b.isAggregateAtom()) { 00213 const AggregateAtom& aatom = lsc.reg->aatoms.getByID(b); 00214 if (aatom.tuple[1].address == ID::TERM_BUILTIN_EQ) lsc.addBoundedVariable(LiberalSafetyChecker::VariableLocation(ruleID, aatom.tuple[0])); 00215 if (aatom.tuple[3].address == ID::TERM_BUILTIN_EQ) lsc.addBoundedVariable(LiberalSafetyChecker::VariableLocation(ruleID, aatom.tuple[4])); 00216 } 00217 00218 // 2 00219 else if (b.isBuiltinAtom()) { 00220 const BuiltinAtom& batom = lsc.reg->batoms.getByID(b); 00221 if (batom.tuple[0].address == ID::TERM_BUILTIN_INT && batom.tuple[1].isVariableTerm()) lsc.addBoundedVariable(LiberalSafetyChecker::VariableLocation(ruleID, batom.tuple[1])); 00222 } 00223 } 00224 } 00225 } 00226 }; 00227 00228 // Exploits well-orderings in cycles to ensure safety 00229 class BenignCycleChecker : public LiberalSafetyPlugin 00230 { 00231 private: 00232 std::set<LiberalSafetyChecker::Node>& cyclicAttributes; 00233 00234 void identifyBenignCycles() { 00235 00236 for (uint32_t c = 0; c < lsc.getDepSCC().size(); ++c) { 00237 // check for this SCC: 00238 // 1. if it is cyclic 00239 // 2. the SCC has potential to become malign 00240 if (lsc.getDepSCC()[c].size() > 1) { 00241 DBGLOG(DBG, "Checking if cycle " << c << " is benign"); 00242 bool malign = false; 00243 00244 // stores for each external atom ID the pairs of input and output arguments which need to support a wellordering 00245 std::vector<std::pair<ID, std::pair<int, int> > > pairsToCheck; 00246 00247 // for all output attributes 00248 BOOST_FOREACH (LiberalSafetyChecker::Attribute oat, lsc.getDepSCC()[c]) { 00249 if (oat.type == LiberalSafetyChecker::Attribute::External && oat.input == false && lsc.getDomainExpansionSafeAttributes().count(oat) == 0) { 00250 // for all corresponding input attributes which are not bounded 00251 BOOST_FOREACH (LiberalSafetyChecker::Attribute iat, lsc.getDepSCC()[c]) { 00252 if (iat.type == LiberalSafetyChecker::Attribute::External && iat.input == true && iat.eatomID == oat.eatomID && iat.ruleID == oat.ruleID && lsc.getDomainExpansionSafeAttributes().count(iat) == 0) { 00253 // store this pair 00254 pairsToCheck.push_back(std::pair<ID, std::pair<int, int> >(iat.eatomID, std::pair<int, int>(iat.argIndex - 1, oat.argIndex - 1))); 00255 } 00256 } 00257 } 00258 } 00259 00260 // check all pairs 00261 bool strlen = true; 00262 bool natural = true; 00263 for (uint32_t p = 0; p < pairsToCheck.size(); ++p) { 00264 DBGLOG(DBG, "Checking if " << pairsToCheck[p].first << " has a wellordering from argument " << pairsToCheck[p].second.first << " to argument " << pairsToCheck[p].second.second); 00265 const ExtSourceProperties& prop = lsc.reg->eatoms.getByID(pairsToCheck[p].first).getExtSourceProperties(); 00266 strlen &= prop.hasWellorderingStrlen(pairsToCheck[p].second.first, pairsToCheck[p].second.second); 00267 natural &= prop.hasWellorderingNatural(pairsToCheck[p].second.first, pairsToCheck[p].second.second); 00268 } 00269 malign = !strlen && !natural; 00270 00271 if (!malign) { 00272 DBGLOG(DBG, "Cycle is benign"); 00273 00274 // make all output variables of external atoms in the component bounded 00275 BOOST_FOREACH (LiberalSafetyChecker::Attribute oat, lsc.getDepSCC()[c]) { 00276 if (oat.type == LiberalSafetyChecker::Attribute::External && oat.input == false) { 00277 const ExternalAtom& eatom = lsc.reg->eatoms.getByID(oat.eatomID); 00278 BOOST_FOREACH (ID var, lsc.reg->getVariablesInID(eatom.tuple[oat.argIndex - 1])) { 00279 LiberalSafetyChecker::VariableLocation vl(oat.ruleID, var); 00280 if (lsc.getBoundedVariables().count(vl) == 0) { 00281 lsc.addExternallyBoundedVariable(oat.eatomID, vl); 00282 } 00283 } 00284 } 00285 } 00286 } 00287 } 00288 } 00289 } 00290 00291 void computeCyclicAttributes() { 00292 00293 // find cyclic external attributes 00294 std::vector<LiberalSafetyChecker::Attribute> cyclicExternal; 00295 for (uint32_t c = 0; c < lsc.getDepSCC().size(); ++c) { 00296 // check for this SCC if it contains an unsafe cyclic external attribute 00297 if (lsc.getDepSCC()[c].size() > 1) { 00298 bool external = false; 00299 BOOST_FOREACH (LiberalSafetyChecker::Attribute oat, lsc.getDepSCC()[c]) { 00300 if (oat.type == LiberalSafetyChecker::Attribute::External && oat.input == false && lsc.getDomainExpansionSafeAttributes().count(oat) == 0) { 00301 external = true; 00302 break; 00303 } 00304 } 00305 if (external) { 00306 BOOST_FOREACH (LiberalSafetyChecker::Attribute at, lsc.getDepSCC()[c]) { 00307 if (at.type == LiberalSafetyChecker::Attribute::External) { 00308 DBGLOG(DBG, "Found cyclic external attribute of " << at.predicate); 00309 cyclicExternal.push_back(at); 00310 } 00311 } 00312 } 00313 } 00314 } 00315 00316 // find all attributes which depend on cyclic external attributes 00317 cyclicAttributes.clear(); 00318 BOOST_FOREACH (LiberalSafetyChecker::Attribute at, cyclicExternal) { 00319 lsc.getReachableAttributes(at, cyclicAttributes); 00320 } 00321 DBGLOG(DBG, "" << cyclicAttributes.size() << " attributes depend cyclically on external attributes"); 00322 } 00323 00324 public: 00325 BenignCycleChecker(LiberalSafetyChecker& lsc, std::set<LiberalSafetyChecker::Node>& cyclicAttributes) : LiberalSafetyPlugin(lsc), cyclicAttributes(cyclicAttributes){} 00326 00327 void run() { 00328 // identify benign cycles 00329 identifyBenignCycles(); 00330 00331 // recompute attributes which depend on malign cycles 00332 computeCyclicAttributes(); 00333 00334 // make all attributes safe, except those in cyclicAttributes 00335 LiberalSafetyChecker::NodeIterator it, it_end; 00336 for(boost::tie(it, it_end) = boost::vertices(lsc.getAttributeGraph()); it != it_end; ++it) { 00337 if (cyclicAttributes.count(*it) == 0) { 00338 DBGLOG(DBG, "Attribute " << lsc.getAttributeGraph()[*it] << " is externally acyclic"); 00339 lsc.addDomainExpansionSafeAttribute(lsc.getAttributeGraph()[*it]); 00340 } 00341 } 00342 } 00343 }; 00344 00345 } 00346 00347 00348 bool LiberalSafetyChecker::Attribute::operator==(const Attribute& at2) const 00349 { 00350 return type == at2.type && 00351 predicate == at2.predicate && 00352 inputList == at2.inputList && 00353 ruleID == at2.ruleID && 00354 input == at2.input && 00355 argIndex == at2.argIndex; 00356 } 00357 00358 00359 bool LiberalSafetyChecker::Attribute::operator<(const Attribute& at2) const 00360 { 00361 if (type < at2.type) return true; 00362 if (predicate < at2.predicate) return true; 00363 if (inputList.size() < at2.inputList.size()) return true; 00364 for (uint32_t i = 0; i < inputList.size(); ++i) 00365 if (inputList[i] < at2.inputList[i]) return true; 00366 if (ruleID < at2.ruleID) return true; 00367 if (input < at2.input) return true; 00368 if (argIndex < at2.argIndex) return true; 00369 return false; 00370 } 00371 00372 00373 std::ostream& LiberalSafetyChecker::Attribute::print(std::ostream& o) const 00374 { 00375 00376 RawPrinter printer(o, reg); 00377 if (type == Attribute::Ordinary) { 00378 // ordinary attribute 00379 printer.print(predicate); 00380 o << "#"; 00381 o << argIndex; 00382 } 00383 else { 00384 // external attribute 00385 o << "r" << ruleID.address << ":"; 00386 o << "&"; 00387 printer.print(predicate); 00388 o << "["; 00389 for (uint32_t i = 0; i < inputList.size(); ++i) { 00390 if (i > 0) o << ","; 00391 printer.print(inputList[i]); 00392 } 00393 o << "]"; 00394 o << "#"; 00395 o << (input ? "i" : "o"); 00396 o << argIndex; 00397 } 00398 return o; 00399 } 00400 00401 00402 LiberalSafetyChecker::Attribute LiberalSafetyChecker::getAttribute(ID eatomID, ID predicate, std::vector<ID> inputList, ID ruleID, bool inputAttribute, int argumentIndex) 00403 { 00404 00405 Attribute at; 00406 at.reg = reg; 00407 at.type = Attribute::External; 00408 at.ruleID = ruleID; 00409 at.eatomID = eatomID; 00410 at.predicate = predicate; 00411 at.inputList = inputList; 00412 at.input = inputAttribute; 00413 at.argIndex = argumentIndex; 00414 return at; 00415 } 00416 00417 00418 LiberalSafetyChecker::Attribute LiberalSafetyChecker::getAttribute(ID predicate, int argumentIndex) 00419 { 00420 00421 Attribute at; 00422 at.reg = reg; 00423 at.type = Attribute::Ordinary; 00424 at.ruleID = ID_FAIL; 00425 at.eatomID = ID_FAIL; 00426 at.predicate = predicate; 00427 at.input = false; 00428 at.argIndex = argumentIndex; 00429 predicateArity[predicate] = argumentIndex > predicateArity[predicate] ? argumentIndex : predicateArity[predicate]; 00430 return at; 00431 } 00432 00433 00434 LiberalSafetyChecker::Node LiberalSafetyChecker::getNode(Attribute at) 00435 { 00436 00437 const NodeNodeInfoIndex& idx = nm.get<NodeInfoTag>(); 00438 NodeNodeInfoIndex::const_iterator it = idx.find(at); 00439 if(it != idx.end()) { 00440 return it->node; 00441 } 00442 else { 00443 Node n = boost::add_vertex(at, ag); 00444 if (at.type == Attribute::Ordinary) attributesOfPredicate[at.predicate].push_back(at); 00445 NodeNodeInfoIndex::const_iterator it; 00446 bool success; 00447 boost::tie(it, success) = nm.insert(NodeMappingInfo(at, n)); 00448 assert(success); 00449 return n; 00450 } 00451 } 00452 00453 00454 bool LiberalSafetyChecker::hasInformationFlow(boost::unordered_map<ID, boost::unordered_set<ID> >& builtinflow, ID from, ID to) 00455 { 00456 return from == to || builtinflow[from].count(to) > 0; 00457 } 00458 00459 00460 bool LiberalSafetyChecker::isNewlySafe(Attribute at) 00461 { 00462 return safetyPreconditions[at].first.size() == 0 && safetyPreconditions[at].second.size() == 0; 00463 } 00464 00465 00466 void LiberalSafetyChecker::addExternallyBoundedVariable(ID extAtom, VariableLocation vl) 00467 { 00468 boundedByExternals.insert(std::pair<ID, VariableLocation>(extAtom, vl)); 00469 } 00470 00471 00472 void LiberalSafetyChecker::addBoundedVariable(VariableLocation vl) 00473 { 00474 00475 if (boundedVariables.count(vl) > 0) return; 00476 00477 #ifndef NDEBUG 00478 std::stringstream ss; 00479 RawPrinter printer(ss, reg); 00480 printer.print(vl.second); 00481 DBGLOG(DBG, "Variable " << "r" << vl.first.address << "/" << ss.str() << " is bounded"); 00482 #endif 00483 boundedVariables.insert(vl); 00484 00485 // notify all attributes which wait for this variable to become bounded 00486 while (attributesSafeByVariable[vl].size() > 0) { 00487 Attribute sat = *attributesSafeByVariable[vl].begin(); 00488 DBGLOG(DBG, "Fulfilled precondition of attribute " << sat); 00489 attributesSafeByVariable[vl].erase(attributesSafeByVariable[vl].begin()); 00490 00491 safetyPreconditions[sat].first.erase(vl); 00492 if (isNewlySafe(sat)) { 00493 addDomainExpansionSafeAttribute(sat); 00494 } 00495 } 00496 attributesSafeByVariable[vl].clear(); 00497 00498 // trigger depending actions 00499 BOOST_FOREACH (AtomLocation al, variableOccursIn[vl]) { 00500 00501 // go through all external atoms where: 00502 // 1. the variable occurs in an output position --> then the corresponding output attribute becomes safe 00503 // 2. the variable occurs in an output position and the external atom has a finite fiber --> then the input variables are bounded as well 00504 if (al.second.isExternalAtom()) { 00505 00506 // 1. 00507 const ExternalAtom& eatom = reg->eatoms.getByID(al.second); 00508 for (uint32_t i = 0; i < eatom.tuple.size(); ++i) { 00509 if (eatom.tuple[i] == vl.second) { 00510 Attribute oat = getAttribute(al.second, eatom.predicate, eatom.inputs, al.first, false, i + 1); 00511 if (domainExpansionSafeAttributes.count(oat) == 0) { 00512 addDomainExpansionSafeAttribute(oat); 00513 } 00514 } 00515 } 00516 00517 // 2. 00518 if (eatom.getExtSourceProperties().hasFiniteFiber()) { 00519 bool outputbound = true; 00520 BOOST_FOREACH (ID var, reg->getVariablesInTuple(eatom.tuple)) { 00521 if (boundedVariables.count(VariableLocation(al.first, var)) == 0) { 00522 outputbound = false; 00523 break; 00524 } 00525 } 00526 if (outputbound) { 00527 // bound the input as well 00528 BOOST_FOREACH (ID var, reg->getVariablesInTuple(eatom.inputs)) { 00529 addExternallyBoundedVariable(al.second, VariableLocation(al.first, var)); 00530 } 00531 } 00532 } 00533 } 00534 00535 // go through equivalence builtins 00536 else if (al.second.isBuiltinAtom()) { 00537 const BuiltinAtom& batom = reg->batoms.getByID(al.second); 00538 bool allsafe = true; 00539 // for ternary: if all variables on the rhs are safe, then the variables on the lhs are safe as well 00540 if (batom.tuple.size() == 4) { 00541 for (int i = 1; i <= 2; ++i) { 00542 if (batom.tuple[i].isVariableTerm() && boundedVariables.count(VariableLocation(al.first, batom.tuple[i])) == 0) { 00543 allsafe = false; 00544 break; 00545 } 00546 } 00547 if (allsafe) addBoundedVariable(VariableLocation(al.first, batom.tuple[3])); 00548 00549 // for binary: if one side is safe, then the other side is safe as well 00550 } 00551 else if (batom.tuple.size() == 3 && batom.tuple[0].address == ID::TERM_BUILTIN_EQ) { 00552 if (batom.tuple[1].isVariableTerm() && boundedVariables.count(VariableLocation(al.first, batom.tuple[1])) > 0) addBoundedVariable(VariableLocation(al.first, batom.tuple[2])); 00553 if (batom.tuple[2].isVariableTerm() && boundedVariables.count(VariableLocation(al.first, batom.tuple[2])) > 0) addBoundedVariable(VariableLocation(al.first, batom.tuple[1])); 00554 } 00555 } 00556 } 00557 } 00558 00559 00560 void LiberalSafetyChecker::addDomainExpansionSafeAttribute(Attribute at) 00561 { 00562 00563 // go through all atoms where the attribute occurs 00564 if (domainExpansionSafeAttributes.count(at) > 0) return; 00565 DBGLOG(DBG, "Attribute " << at << " is domain-expansion safe"); 00566 domainExpansionSafeAttributes.insert(at); 00567 00568 // notify all attributes which wait for this attribute to become domain-expansion safe 00569 while (attributesSafeByAttribute[at].size() > 0) { 00570 Attribute sat = *attributesSafeByAttribute[at].begin(); 00571 DBGLOG(DBG, "Fulfilled precondition of attribute " << sat); 00572 attributesSafeByAttribute[at].erase(attributesSafeByAttribute[at].begin()); 00573 00574 assert(std::find(safetyPreconditions[sat].second.begin(), safetyPreconditions[sat].second.end(), at) != safetyPreconditions[sat].second.end()); 00575 safetyPreconditions[sat].second.erase(at); 00576 if (isNewlySafe(sat)) { 00577 addDomainExpansionSafeAttribute(sat); 00578 } 00579 } 00580 00581 // trigger depending actions 00582 // safe attributes may lead to safe variables 00583 // process safe variables due to ordinary atoms first (we want to use external atoms as rarely as possible in order to optimize them away) 00584 BOOST_FOREACH (AtomLocation al, attributeOccursIn[at]) { 00585 if (al.second.isOrdinaryAtom()) { 00586 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(al.second); 00587 BOOST_FOREACH (ID var, reg->getVariablesInID(oatom.tuple[at.argIndex])) { 00588 addBoundedVariable(VariableLocation(al.first, var)); 00589 } 00590 } 00591 if (al.second.isExternalAtom()) { 00592 const ExternalAtom& eatom = reg->eatoms.getByID(al.second); 00593 for (uint32_t o = 0; o < eatom.tuple.size(); ++o) { 00594 if (getAttribute(al.second, eatom.predicate, eatom.inputs, al.first, false, o + 1) == at) { 00595 BOOST_FOREACH (ID var, reg->getVariablesInID(eatom.tuple[o])) { 00596 VariableLocation vl(al.first, var); 00597 00598 // here we COULD bound vl, but we don't do it yet, because 00599 // we want to check first if we can also make it safe without exploiting the external atom 00600 // (this would have the advantage that we can optimize the external atom away) 00601 addExternallyBoundedVariable(al.second, vl); 00602 } 00603 } 00604 } 00605 } 00606 } 00607 } 00608 00609 00610 const std::vector<ID>& LiberalSafetyChecker::getIdb() 00611 { 00612 return idb; 00613 } 00614 00615 00616 const LiberalSafetyChecker::Graph& LiberalSafetyChecker::getAttributeGraph() 00617 { 00618 return ag; 00619 } 00620 00621 00622 const std::vector<std::vector<LiberalSafetyChecker::Attribute> >& LiberalSafetyChecker::getDepSCC() 00623 { 00624 return depSCC; 00625 } 00626 00627 00628 const boost::unordered_set<LiberalSafetyChecker::Attribute>& LiberalSafetyChecker::getDomainExpansionSafeAttributes() 00629 { 00630 return domainExpansionSafeAttributes; 00631 } 00632 00633 00634 const boost::unordered_set<LiberalSafetyChecker::VariableLocation>& LiberalSafetyChecker::getBoundedVariables() 00635 { 00636 return boundedVariables; 00637 } 00638 00639 00640 void LiberalSafetyChecker::getReachableAttributes(Attribute start, std::set<LiberalSafetyChecker::Node>& output) 00641 { 00642 const LiberalSafetyChecker::NodeNodeInfoIndex& idx = nm.get<NodeInfoTag>(); 00643 LiberalSafetyChecker::NodeNodeInfoIndex::const_iterator it = idx.find(start); 00644 boost::breadth_first_search(ag, it->node, 00645 boost::visitor( 00646 boost::make_bfs_visitor( 00647 boost::write_property( 00648 boost::identity_property_map(), 00649 std::inserter(output, output.end()), 00650 boost::on_discover_vertex())))); 00651 } 00652 00653 00654 int LiberalSafetyChecker::getPredicateArity(ID predicate) const 00655 { 00656 return predicateArity.at(predicate); 00657 } 00658 00659 00660 void LiberalSafetyChecker::computeBuiltinInformationFlow(const Rule& rule, boost::unordered_map<ID, boost::unordered_set<ID> >& builtinflow) 00661 { 00662 00663 BOOST_FOREACH (ID b, rule.body) { 00664 if (!b.isNaf() && b.isBuiltinAtom()) { 00665 DBGLOG(DBG, "Computing information flow in builtin atom " << b); 00666 const BuiltinAtom& batom = reg->batoms.getByID(b); 00667 if (batom.tuple[0].address == ID::TERM_BUILTIN_ADD || batom.tuple[0].address == ID::TERM_BUILTIN_SUB || batom.tuple[0].address == ID::TERM_BUILTIN_MUL || batom.tuple[0].address == ID::TERM_BUILTIN_DIV || batom.tuple[0].address == ID::TERM_BUILTIN_MOD) { 00668 // information from right to left 00669 if (batom.tuple[1].isVariableTerm()) { 00670 DBGLOG(DBG, "Information flow from " << batom.tuple[1] << " to " << batom.tuple[3]); 00671 DBGLOG(DBG, "Information flow from " << batom.tuple[2] << " to " << batom.tuple[3]); 00672 if (batom.tuple[1].isVariableTerm()) builtinflow[batom.tuple[1]].insert(batom.tuple[3]); 00673 if (batom.tuple[2].isVariableTerm()) builtinflow[batom.tuple[2]].insert(batom.tuple[3]); 00674 } 00675 } 00676 if (batom.tuple[0].address == ID::TERM_BUILTIN_EQ || batom.tuple[0].address == ID::TERM_BUILTIN_SUCC) { 00677 // information flow in both directions 00678 if (batom.tuple[1].isVariableTerm() && batom.tuple[2].isVariableTerm()) { 00679 DBGLOG(DBG, "Information flow from " << batom.tuple[1] << " to " << batom.tuple[2]); 00680 DBGLOG(DBG, "Information flow from " << batom.tuple[2] << " to " << batom.tuple[1]); 00681 builtinflow[batom.tuple[1]].insert(batom.tuple[2]); 00682 builtinflow[batom.tuple[2]].insert(batom.tuple[1]); 00683 } 00684 } 00685 } 00686 } 00687 00688 } 00689 00690 00691 void LiberalSafetyChecker::createDependencyGraph() 00692 { 00693 00694 std::vector<std::pair<Attribute, ID> > predicateInputs; 00695 00696 DBGLOG(DBG, "LiberalSafetyChecker::createDependencyGraph"); 00697 BOOST_FOREACH (ID ruleID, idb) { 00698 const Rule& rule = reg->rules.getByID(ruleID); 00699 00700 boost::unordered_map<ID, boost::unordered_set<ID> > builtinflow; 00701 computeBuiltinInformationFlow(rule, builtinflow); 00702 00703 // head-body dependencies 00704 BOOST_FOREACH (ID hID, rule.head) { 00705 const OrdinaryAtom& hAtom = reg->lookupOrdinaryAtom(hID); 00706 00707 for (uint32_t hArg = 1; hArg < hAtom.tuple.size(); ++hArg) { 00708 BOOST_FOREACH (ID hVar, reg->getVariablesInID(hAtom.tuple[hArg])) { 00709 Node headNode = getNode(getAttribute(hAtom.tuple[0], hArg)); 00710 00711 BOOST_FOREACH (ID bID, rule.body) { 00712 if (bID.isNaf()) continue; 00713 00714 if (bID.isOrdinaryAtom()) { 00715 const OrdinaryAtom& bAtom = reg->lookupOrdinaryAtom(bID); 00716 00717 for (uint32_t bArg = 1; bArg < bAtom.tuple.size(); ++bArg) { 00718 BOOST_FOREACH (ID bVar, reg->getVariablesInID(bAtom.tuple[bArg])) { 00719 Node bodyNode = getNode(getAttribute(bAtom.tuple[0], bArg)); 00720 00721 if (hasInformationFlow(builtinflow, bVar, hVar)) { 00722 boost::add_edge(bodyNode, headNode, ag); 00723 } 00724 } 00725 } 00726 } 00727 00728 if (bID.isExternalAtom()) { 00729 const ExternalAtom& eAtom = reg->eatoms.getByID(bID); 00730 00731 for (uint32_t bArg = 0; bArg < eAtom.tuple.size(); ++bArg) { 00732 BOOST_FOREACH (ID bVar, reg->getVariablesInID(eAtom.tuple[bArg])) { 00733 Node bodyNode = getNode(getAttribute(bID, eAtom.predicate, eAtom.inputs, ruleID, false, (bArg + 1))); 00734 00735 if (hasInformationFlow(builtinflow, bVar, hVar)) { 00736 boost::add_edge(bodyNode, headNode, ag); 00737 } 00738 } 00739 } 00740 } 00741 } 00742 } 00743 } 00744 } 00745 00746 // body-body dependencies 00747 BOOST_FOREACH (ID bID1, rule.body) { 00748 if (bID1.isNaf()) continue; 00749 00750 if (bID1.isOrdinaryAtom()) { 00751 const OrdinaryAtom& bAtom = reg->lookupOrdinaryAtom(bID1); 00752 00753 for (uint32_t bArg1 = 1; bArg1 < bAtom.tuple.size(); ++bArg1) { 00754 BOOST_FOREACH (ID bVar1, reg->getVariablesInID(bAtom.tuple[bArg1])) { 00755 Node bodyNode1 = getNode(getAttribute(bAtom.tuple[0], bArg1)); 00756 00757 BOOST_FOREACH (ID bID2, rule.body) { 00758 if (bID2.isNaf()) continue; 00759 00760 if (bID2.isExternalAtom()) { 00761 const ExternalAtom& eAtom = reg->eatoms.getByID(bID2); 00762 00763 for (uint32_t bArg2 = 0; bArg2 < eAtom.inputs.size(); ++bArg2) { 00764 BOOST_FOREACH (ID bVar2, reg->getVariablesInID(eAtom.inputs[bArg2])) { 00765 Node bodyNode2 = getNode(getAttribute(bID2, eAtom.predicate, eAtom.inputs, ruleID, true, (bArg2 + 1))); 00766 if (hasInformationFlow(builtinflow, bVar1, bVar2)) { 00767 boost::add_edge(bodyNode1, bodyNode2, ag); 00768 } 00769 } 00770 } 00771 } 00772 } 00773 } 00774 } 00775 } 00776 if (bID1.isExternalAtom()) { 00777 const ExternalAtom& eAtom1 = reg->eatoms.getByID(bID1); 00778 00779 for (uint32_t bArg1 = 0; bArg1 < eAtom1.tuple.size(); ++bArg1) { 00780 BOOST_FOREACH (ID bVar1, reg->getVariablesInID(eAtom1.tuple[bArg1])) { 00781 Node bodyNode1 = getNode(getAttribute(bID1, eAtom1.predicate, eAtom1.inputs, ruleID, false, (bArg1 + 1))); 00782 00783 BOOST_FOREACH (ID bID2, rule.body) { 00784 if (bID2.isNaf()) continue; 00785 00786 if (bID2.isExternalAtom()) { 00787 const ExternalAtom& eAtom2 = reg->eatoms.getByID(bID2); 00788 00789 for (uint32_t bArg2 = 0; bArg2 < eAtom2.inputs.size(); ++bArg2) { 00790 BOOST_FOREACH (ID bVar2, reg->getVariablesInID(eAtom2.inputs[bArg2])) { 00791 Node bodyNode2 = getNode(getAttribute(bID2, eAtom2.predicate, eAtom2.inputs, ruleID, true, (bArg2 + 1))); 00792 00793 if (bVar1 == bVar2) { 00794 boost::add_edge(bodyNode1, bodyNode2, ag); 00795 } 00796 } 00797 } 00798 } 00799 } 00800 } 00801 } 00802 } 00803 } 00804 00805 // EA input-output dependencies 00806 BOOST_FOREACH (ID bID, rule.body) { 00807 if (bID.isNaf()) continue; 00808 00809 if (bID.isExternalAtom()) { 00810 const ExternalAtom& eAtom = reg->eatoms.getByID(bID); 00811 00812 for (uint32_t i = 0; i < eAtom.inputs.size(); ++i) { 00813 Node inputNode = getNode(getAttribute(bID, eAtom.predicate, eAtom.inputs, ruleID, true, (i + 1))); 00814 for (uint32_t o = 0; o < eAtom.tuple.size(); ++o) { 00815 Node outputNode = getNode(getAttribute(bID, eAtom.predicate, eAtom.inputs, ruleID, false, (o + 1))); 00816 boost::add_edge(inputNode, outputNode, ag); 00817 } 00818 if (eAtom.pluginAtom->getInputType(i) == PluginAtom::PREDICATE) { 00819 predicateInputs.push_back(std::pair<Attribute, ID>(getAttribute(bID, eAtom.predicate, eAtom.inputs, ruleID, true, (i + 1)), eAtom.inputs[i])); 00820 } 00821 } 00822 } 00823 } 00824 } 00825 00826 // connect predicate input attributes 00827 typedef std::pair<Attribute, ID> AttPredPair; 00828 BOOST_FOREACH (AttPredPair p, predicateInputs) { 00829 BOOST_FOREACH (Attribute ordinaryPredicateAttribute, attributesOfPredicate[p.second]) { 00830 boost::add_edge(getNode(ordinaryPredicateAttribute), getNode(p.first), ag); 00831 } 00832 } 00833 00834 // find strongly connected components in the graph 00835 DBGLOG(DBG, "Computing strongly connected components in attribute dependency graph"); 00836 std::vector<int> componentMap(num_vertices(ag)); 00837 int num = boost::strong_components(ag, boost::make_iterator_property_map(componentMap.begin(), get(boost::vertex_index, ag))); 00838 depSCC = std::vector<std::vector<Attribute> >(num); 00839 int nodeNr = 0; 00840 BOOST_FOREACH (int componentOfNode, componentMap) { 00841 depSCC[componentOfNode].push_back(ag[nodeNr++]); 00842 } 00843 } 00844 00845 00846 void LiberalSafetyChecker::createPreconditionsAndLocationIndices() 00847 { 00848 00849 BOOST_FOREACH (ID ruleID, idb) { 00850 const Rule& rule = reg->rules.getByID(ruleID); 00851 00852 // store for each attribute of a head atom the variable on which it depends 00853 BOOST_FOREACH (ID hID, rule.head) { 00854 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(hID); 00855 for (uint32_t i = 1; i < oatom.tuple.size(); ++i) { 00856 BOOST_FOREACH (ID var, reg->getVariablesInID(oatom.tuple[i])) { 00857 safetyPreconditions[getAttribute(oatom.tuple[0], i)].first.insert(VariableLocation(ruleID, var)); 00858 attributesSafeByVariable[VariableLocation(ruleID, var)].insert(getAttribute(oatom.tuple[0], i)); 00859 } 00860 } 00861 } 00862 00863 // 1. store for body attributes in which ordinary or external atoms they occur 00864 // 2. store for external atoms: 00865 // - for which variables they wait to become bounded 00866 // - for which attributes they wait to become domain-expansion safe 00867 BOOST_FOREACH (ID bID, rule.body) { 00868 if (bID.isNaf()) continue; 00869 00870 // attributes which occur in ordinary body atoms 00871 if (bID.isOrdinaryAtom()) { 00872 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(bID); 00873 for (uint32_t i = 1; i < oatom.tuple.size(); ++i) { 00874 attributeOccursIn[getAttribute(oatom.tuple[0], i)].insert(AtomLocation(ruleID, bID)); 00875 BOOST_FOREACH (ID var, reg->getVariablesInID(oatom.tuple[i])) { 00876 variableOccursIn[VariableLocation(ruleID, var)].insert(AtomLocation(ruleID, bID)); 00877 } 00878 } 00879 } 00880 00881 // attributes which occur as predicate input to external atoms 00882 // also store the preconditions for an external attribute to become domain-expansion safe 00883 else if (bID.isExternalAtom()) { 00884 const ExternalAtom& eatom = reg->eatoms.getByID(bID); 00885 for (uint32_t i = 0; i < eatom.inputs.size(); ++i) { 00886 Attribute iattr = getAttribute(bID, eatom.predicate, eatom.inputs, ruleID, true, i + 1); 00887 00888 // for predicate input parameters, we have to wait for all attributes of the according predicate to become safe 00889 if (eatom.pluginAtom->getInputType(i) == PluginAtom::PREDICATE) { 00890 for (int a = 1; a <= predicateArity[eatom.inputs[i]]; ++a) { 00891 attributeOccursIn[getAttribute(eatom.inputs[i], a)].insert(AtomLocation(ruleID, bID)); 00892 safetyPreconditions[iattr].second.insert(getAttribute(eatom.inputs[i], a)); 00893 attributesSafeByAttribute[getAttribute(eatom.inputs[i], a)].insert(iattr); 00894 } 00895 } 00896 // for variables in place of constant parameters, we have to wait for the variable to become bounded 00897 BOOST_FOREACH (ID var, reg->getVariablesInID(eatom.inputs[i])) { 00898 if (eatom.pluginAtom->getInputType(i) != PluginAtom::PREDICATE) { 00899 safetyPreconditions[iattr].first.insert(VariableLocation(ruleID, var)); 00900 attributesSafeByVariable[VariableLocation(ruleID, var)].insert(iattr); 00901 variableOccursIn[VariableLocation(ruleID, var)].insert(AtomLocation(ruleID, bID)); 00902 } 00903 } 00904 00905 // for output attributes, we have to wait for all input attributes to become safe 00906 for (uint32_t o = 0; o < eatom.tuple.size(); ++o) { 00907 Attribute oattr = getAttribute(bID, eatom.predicate, eatom.inputs, ruleID, false, o + 1); 00908 attributeOccursIn[oattr].insert(AtomLocation(ruleID, bID)); 00909 safetyPreconditions[oattr].second.insert(iattr); 00910 attributesSafeByAttribute[iattr].insert(oattr); 00911 } 00912 } 00913 for (uint32_t i = 0; i < eatom.tuple.size(); ++i) { 00914 variableOccursIn[VariableLocation(ruleID, eatom.tuple[i])].insert(AtomLocation(ruleID, bID)); 00915 } 00916 } 00917 00918 // remember the variables which occur in builtin atoms 00919 else if (bID.isBuiltinAtom()) { 00920 const BuiltinAtom& batom = reg->batoms.getByID(bID); 00921 std::set<ID> vars; 00922 reg->getVariablesInID(bID, vars); 00923 BOOST_FOREACH (ID v, vars) variableOccursIn[VariableLocation(ruleID, v)].insert(AtomLocation(ruleID, bID)); 00924 } 00925 } 00926 } 00927 } 00928 00929 00930 void LiberalSafetyChecker::computeCyclicAttributes() 00931 { 00932 00933 // find cyclic external attributes 00934 std::vector<Attribute> cyclicExternal; 00935 for (uint32_t c = 0; c < depSCC.size(); ++c) { 00936 // check for this SCC if it contains an unsafe cyclic external attribute 00937 if (depSCC[c].size() > 1) { 00938 bool external = false; 00939 BOOST_FOREACH (Attribute oat, depSCC[c]) { 00940 if (oat.type == Attribute::External && oat.input == false && domainExpansionSafeAttributes.count(oat) == 0) { 00941 external = true; 00942 break; 00943 } 00944 } 00945 if (external) { 00946 BOOST_FOREACH (Attribute at, depSCC[c]) { 00947 if (at.type == Attribute::External) { 00948 DBGLOG(DBG, "Found cyclic external attribute of " << at.predicate); 00949 cyclicExternal.push_back(at); 00950 } 00951 } 00952 } 00953 } 00954 } 00955 00956 // find all attributes which depend on cyclic external attributes 00957 cyclicAttributes.clear(); 00958 BOOST_FOREACH (Attribute at, cyclicExternal) { 00959 const NodeNodeInfoIndex& idx = nm.get<NodeInfoTag>(); 00960 NodeNodeInfoIndex::const_iterator it = idx.find(at); 00961 boost::breadth_first_search(ag, it->node, 00962 boost::visitor( 00963 boost::make_bfs_visitor( 00964 boost::write_property( 00965 boost::identity_property_map(), 00966 std::inserter(cyclicAttributes, cyclicAttributes.end()), 00967 boost::on_discover_vertex())))); 00968 } 00969 DBGLOG(DBG, "" << cyclicAttributes.size() << " attributes depend cyclically on external attributes"); 00970 } 00971 00972 00973 void LiberalSafetyChecker::ensureOrdinarySafety() 00974 { 00975 00976 // if a variable occurs in no ordinary atom and no necessary external atom, add an additional necessary external atom 00977 BOOST_FOREACH (ID ruleID, idb) { 00978 const Rule& rule = reg->rules.getByID(ruleID); 00979 00980 // check if the rule is still safe if all external atoms, which are not necessary to ensure domain-expansion safety, are removed 00981 bool safe = false; 00982 while (!safe) { 00983 safe = true; // assumption 00984 00985 // now construct the optimized rule 00986 DBGLOG(DBG, "Constructing optimized rule"); 00987 Rule optimizedRule(rule.kind); 00988 optimizedRule.head = rule.head; 00989 optimizedRule.headGuard = rule.headGuard; 00990 BOOST_FOREACH (ID b, rule.body) { 00991 if (!b.isNaf() && b.isExternalAtom() && necessaryExternalAtoms.count(b.address) == 0) continue; 00992 optimizedRule.body.push_back(b); 00993 } 00994 ID optimizedRuleID = (optimizedRule.head.size() > 0 || optimizedRule.body.size() > 0) ? reg->storeRule(optimizedRule) : ruleID; 00995 00996 // safety check 00997 DBGLOG(DBG, "Checking safety of optimized rule"); 00998 ProgramCtx ctx2; 00999 ctx2.setupRegistry(reg); 01000 ctx2.idb.push_back(optimizedRuleID); 01001 SafetyChecker sc(ctx2); 01002 01003 Tuple unsafeVariables = sc.checkSafety(false); 01004 01005 // check if the optimized rule contains all variables of the original rule 01006 DBGLOG(DBG, "Checking variables of optimized rule"); 01007 std::set<ID> varOrig = reg->getVariablesInTuple(rule.body); 01008 std::set<ID> varOpt = reg->getVariablesInTuple(optimizedRule.body); 01009 BOOST_FOREACH (ID vo, varOrig) { 01010 if (varOpt.count(vo) == 0) unsafeVariables.push_back(vo); 01011 } 01012 01013 std::set<ID> searchFor; 01014 BOOST_FOREACH (ID v, unsafeVariables) searchFor.insert(v); 01015 if (unsafeVariables.size() == 0) { 01016 // safe 01017 DBGLOG(DBG, "Optimized rule is safe"); 01018 } 01019 else { 01020 // unsafe 01021 DBGLOG(DBG, "Optimized rule is unsafe"); 01022 safe = false; 01023 // add a not necessary external atom which binds at least one unsafe variable 01024 ID newSafeVar = ID_FAIL; 01025 BOOST_FOREACH (ID b, rule.body) { 01026 if (!b.isNaf() && b.isExternalAtom() && necessaryExternalAtoms.count(b.address) == 0) { 01027 const ExternalAtom& eatom = reg->eatoms.getByID(b); 01028 // for (int i = 0; i < eatom.tuple.size(); ++i){ 01029 BOOST_FOREACH (ID var, reg->getVariablesInTuple(eatom.tuple)) { 01030 if (searchFor.count(var) > 0) { 01031 DBGLOG(DBG, "Adding external atom " << b << " to the necessary ones for reasons of ordinary safety"); 01032 necessaryExternalAtoms.insert(b.address); 01033 // breakout: do not add further external atoms but recheck safety first 01034 newSafeVar = var; 01035 break; 01036 } 01037 } 01038 // just for optimization 01039 if (newSafeVar != ID_FAIL) break; 01040 } 01041 } 01042 // at least one atom must have been added 01043 assert(newSafeVar != ID_FAIL); 01044 } 01045 } 01046 } 01047 } 01048 01049 01050 void LiberalSafetyChecker::computeDomainExpansionSafety() 01051 { 01052 01053 // We employ the following general strategy: 01054 // 1. check static conditions which make attributes domain-expansion safe or variables bounded 01055 // (conditions which do not depend on previously domain-expansion safe attributes or bounded variables) 01056 // 01057 // while (not domain-expansion safe && changes){ 01058 // 2. check dynamic conditions which make attributes domain-expansion safe or variables bounded 01059 // (conditions which depend on previously domain-expansion safe attributes or bounded variables) 01060 // } 01061 // 01062 // For implementing step 2 we further exploit the following ideas: 01063 // - Do not recheck conditions if no relevant precondition changed; 01064 // Use triggers as often as possible: new safe attributes or bounded variables may imply further safe attributes or bounded variables 01065 // - Only make use of external atoms if this is absolutely necessary 01066 // (if safety can be established without external atoms, then grounding will be easier) 01067 // 01068 01069 bool changed = true; 01070 while (!isDomainExpansionSafe() && changed) { 01071 changed = false; 01072 01073 int bvsize = boundedVariables.size(); 01074 int desize = domainExpansionSafeAttributes.size(); 01075 01076 // call safety providers 01077 BOOST_FOREACH (LiberalSafetyPluginPtr checker, safetyPlugins) { 01078 checker->run(); 01079 } 01080 01081 if (boundedVariables.size() != bvsize) changed = true; 01082 if (domainExpansionSafeAttributes.size() != desize) changed = true; 01083 01084 // exploit external atoms to establish further boundings of variables 01085 while (boundedByExternals.size() > 0) { 01086 VariableLocation vl = boundedByExternals.begin()->second; 01087 ID eatom = boundedByExternals.begin()->first; 01088 boundedByExternals.erase(boundedByExternals.begin()); 01089 if (boundedVariables.count(vl) == 0) { 01090 DBGLOG(DBG, "Exploiting " << eatom); 01091 necessaryExternalAtoms.insert(eatom.address); 01092 addBoundedVariable(vl); 01093 changed = true; 01094 break; 01095 } 01096 } 01097 } 01098 01099 // our optimization technique eliminates external atoms which are not necessary 01100 // to establish domain-expansion safety; 01101 // however, this might also destroy ordinary safety, which has to be avoided now 01102 ensureOrdinarySafety(); 01103 01104 DBGLOG(DBG, "Domain Expansion Safety: " << isDomainExpansionSafe() << " (" << domainExpansionSafeAttributes.size() << " out of " << num_vertices(ag) << " attributes are safe)"); 01105 } 01106 01107 01108 LiberalSafetyChecker::LiberalSafetyChecker(RegistryPtr reg, const std::vector<ID>& idb, std::vector<LiberalSafetyPluginFactoryPtr> customSafetyPlugins) : reg(reg), idb(idb) 01109 { 01110 safetyPlugins.push_back(LiberalSafetyPluginPtr(new FinitenessChecker(*this))); 01111 safetyPlugins.push_back(LiberalSafetyPluginPtr(new FiniteFiberChecker(*this))); 01112 safetyPlugins.push_back(LiberalSafetyPluginPtr(new AggregateAndBuildinChecker(*this))); 01113 safetyPlugins.push_back(LiberalSafetyPluginPtr(new BenignCycleChecker(*this, cyclicAttributes))); 01114 BOOST_FOREACH (LiberalSafetyPluginFactoryPtr lspf, customSafetyPlugins) safetyPlugins.push_back(lspf->create(*this)); 01115 01116 createDependencyGraph(); 01117 createPreconditionsAndLocationIndices(); 01118 computeDomainExpansionSafety(); 01119 } 01120 01121 01122 bool LiberalSafetyChecker::isDomainExpansionSafe() const 01123 { 01124 return domainExpansionSafeAttributes.size() == num_vertices(ag); 01125 } 01126 01127 01128 bool LiberalSafetyChecker::isExternalAtomNecessaryForDomainExpansionSafety(ID eatomID) const 01129 { 01130 if (!isDomainExpansionSafe()) return true; 01131 return necessaryExternalAtoms.count(eatomID.address) > 0; 01132 } 01133 01134 01135 namespace 01136 { 01137 inline std::string graphviz_node_id(LiberalSafetyChecker::Node n) { 01138 std::ostringstream os; 01139 os << "n" << n; 01140 return os.str(); 01141 } 01142 } 01143 01144 01145 void LiberalSafetyChecker::writeGraphViz(std::ostream& o, bool verbose) const 01146 { 01147 01148 DBGLOG(DBG, "LiberalSafetyChecker::writeGraphViz"); 01149 01150 o << "digraph G {" << std::endl; 01151 01152 // print vertices 01153 NodeIterator it, it_end; 01154 for(boost::tie(it, it_end) = boost::vertices(ag); it != it_end; ++it) { 01155 o << graphviz_node_id(*it) << "[label=\""; 01156 { 01157 std::ostringstream ss; 01158 ss << ag[*it]; 01159 graphviz::escape(o, ss.str()); 01160 } 01161 o << "\""; 01162 o << ",shape=box"; 01163 std::vector<std::string> style; 01164 if (cyclicAttributes.count(*it) > 0) { 01165 if (domainExpansionSafeAttributes.count(ag[*it]) == 0) { 01166 o << ",fillcolor=red"; 01167 } 01168 else { 01169 o << ",fillcolor=yellow"; 01170 } 01171 style.push_back("filled"); 01172 } 01173 if (ag[*it].type == Attribute::External && necessaryExternalAtoms.count(ag[*it].eatomID.address) == 0) { 01174 style.push_back("dashed"); 01175 } 01176 o << ",style=\""; 01177 for (uint32_t i = 0; i < style.size(); ++i) o << (i > 0 ? "," : "") << style[i]; 01178 o << "\""; 01179 o << "];" << std::endl; 01180 } 01181 01182 // print edges 01183 DependencyIterator dit, dit_end; 01184 for(boost::tie(dit, dit_end) = boost::edges(ag); dit != dit_end; ++dit) { 01185 Node src = boost::source(*dit, ag); 01186 Node target = boost::target(*dit, ag); 01187 o << graphviz_node_id(src) << " -> " << graphviz_node_id(target) << 01188 "[label=\""; 01189 { 01190 std::ostringstream ss; 01191 } 01192 o << "\"];" << std::endl; 01193 } 01194 01195 o << "}" << std::endl; 01196 } 01197 01198 01199 std::size_t hash_value(const LiberalSafetyChecker::Attribute& at) 01200 { 01201 std::size_t seed = 0; 01202 boost::hash_combine(seed, at.type); 01203 boost::hash_combine(seed, at.eatomID); 01204 boost::hash_combine(seed, at.predicate); 01205 BOOST_FOREACH (ID i, at.inputList) boost::hash_combine(seed, i); 01206 boost::hash_combine(seed, at.ruleID); 01207 boost::hash_combine(seed, at.input); 01208 boost::hash_combine(seed, at.argIndex); 01209 return seed; 01210 } 01211 01212 01213 std::size_t hash_value(const LiberalSafetyChecker::VariableLocation& vl) 01214 { 01215 std::size_t seed = 0; 01216 boost::hash_combine(seed, vl.first); 01217 boost::hash_combine(seed, vl.second); 01218 return seed; 01219 } 01220 01221 01222 DLVHEX_NAMESPACE_END 01223 01224 // vim:expandtab:ts=4:sw=4: 01225 // mode: C++ 01226 // End: