dlvhex  2.5.0
src/LiberalSafetyChecker.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/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: