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 00036 #ifdef HAVE_CONFIG_H 00037 #include "config.h" 00038 #endif // HAVE_CONFIG_H 00039 00040 #include "dlvhex2/SafetyChecker.h" 00041 #include "dlvhex2/Registry.h" 00042 #include "dlvhex2/Printer.h" 00043 00044 #include <fstream> 00045 00046 DLVHEX_NAMESPACE_BEGIN 00047 00048 SafetyCheckerBase::SafetyCheckerBase(const ProgramCtx& ctx): 00049 ctx(ctx) 00050 { 00051 } 00052 00053 00054 SafetyCheckerBase::~SafetyCheckerBase() 00055 { 00056 } 00057 00058 00059 namespace 00060 { 00061 bool isSafeBuiltinPredicate(ID::TermBuiltinAddress pred) { 00062 switch(pred) { 00063 case ID::TERM_BUILTIN_MUL: 00064 case ID::TERM_BUILTIN_ADD: 00065 case ID::TERM_BUILTIN_INT: 00066 case ID::TERM_BUILTIN_SUCC: 00067 // October 10, 2012: added the following three cases; 00068 // it seems that all backends can handle this 00069 case ID::TERM_BUILTIN_SUB: 00070 case ID::TERM_BUILTIN_DIV: 00071 case ID::TERM_BUILTIN_MOD: 00072 return true; 00073 default: 00074 return false; 00075 } 00076 } 00077 } 00078 00079 00080 SafetyChecker::SafetyChecker(const ProgramCtx& ctx): 00081 SafetyCheckerBase(ctx) 00082 { 00083 } 00084 00085 00086 SafetyChecker::~SafetyChecker() 00087 { 00088 } 00089 00090 00091 namespace 00092 { 00093 00094 bool reorderForSafety(RegistryPtr reg, std::list<ID>& src, Tuple& tgt, std::set<ID>& safevars); 00095 00096 bool transferSafeLiteralsAndNewlySafeVariables( 00097 RegistryPtr reg, std::list<ID>& src, Tuple& tgt, const std::set<ID>& safevars, std::set<ID>& newsafevars); 00098 00099 // for each element in src: 00100 // 1) check if it is one of 2.1) a) to e) (see SafetyChecker::operator()) 00101 // 2) if yes 00102 // 2.1) move it from src to tgt 00103 // 2.2) put new safe vars (see 2.2) into newsafevars 00104 // 2.3) if something was previously nottransferred mark as reordered 00105 // 3) if no mark as nottransferred 00106 // return true iff reordering took place: 00107 // condition: some element in src was nottransferred AND some element after that element was transferred 00108 bool transferSafeLiteralsAndNewlySafeVariables( 00109 RegistryPtr reg, std::list<ID>& src, Tuple& tgt, const std::set<ID>& safevars, std::set<ID>& newsafevars) { 00110 assert(!src.empty()); 00111 assert(!!reg); 00112 00113 bool nottransferred = false; 00114 bool reordered = false; 00115 00116 typedef std::list<ID>::iterator ListIterator; 00117 typedef std::set<ID>::const_iterator SetCIterator; 00118 ListIterator it = src.begin(); 00119 while( it != src.end() ) { 00120 DBGLOG(DBG,"checking literal " << printToString<RawPrinter>(*it, reg)); 00121 bool transfer = false; 00122 assert(!it->isAtom() && it->isLiteral()); 00123 if( it->isOrdinaryGroundAtom() ) { 00124 DBGLOG(DBG," -> safe (ordinary ground)"); 00125 transfer = true; 00126 } 00127 else if( it->isNaf() && !it->isAggregateAtom() ) { 00128 DBGLOG(DBG," -> need to check if all variables are safe (NAF and not ground and no aggregate)"); 00129 std::set<ID> vars; 00130 reg->getVariablesInID(*it, vars); 00131 bool good = true; 00132 BOOST_FOREACH(ID idv, vars) { 00133 if( safevars.find(idv) == safevars.end() ) { 00134 good = false; 00135 break; 00136 } 00137 } 00138 if( good ) 00139 transfer = true; 00140 } 00141 // positive nonground 00142 else if( it->isOrdinaryNongroundAtom() ) { 00143 DBGLOG(DBG," -> safe, marking all variables as safe"); 00144 reg->getVariablesInID(*it, newsafevars); 00145 transfer = true; 00146 } 00147 else if( it->isExternalAtom() ) { 00148 DBGLOG(DBG," -> checking input safety"); 00149 const ExternalAtom& atom = reg->eatoms.getByID(*it); 00150 00151 bool good = true; 00152 BOOST_FOREACH(ID idt, reg->getVariablesInTuple(atom.inputs)) { 00153 if( idt.isVariableTerm() && safevars.find(idt) == safevars.end() ) { 00154 good = false; 00155 break; 00156 } 00157 } 00158 00159 if( good ) { 00160 DBGLOG(DBG," -> inputs safe, adding outputs as safe"); 00161 BOOST_FOREACH(ID idt, reg->getVariablesInTuple(atom.tuple)) { 00162 if( idt.isVariableTerm() ) 00163 newsafevars.insert(idt); 00164 } 00165 transfer = true; 00166 } 00167 } 00168 else if( it->isAggregateAtom() ) { 00169 // this is complicated if it is implemented properly: 00170 // so we only do lightweight checking (the backend will complain anyways) 00171 // 00172 // a) get safe variables from aggregate body 00173 // b) if aggregate is an assignment and the aggregate is not in a NAF literal, 00174 // make assigned variable safe 00175 00176 const AggregateAtom& atom = reg->aatoms.getByID(*it); 00177 00178 // a) get safe variables from aggregate body 00179 // (if we cannot consume the body completely, 00180 // we have to wait with checking this aggregate) 00181 00182 std::list<ID> tmpSrcBody(atom.literals.begin(), atom.literals.end()); 00183 Tuple tmpTgt; 00184 tmpTgt.reserve(atom.literals.size()); 00185 std::set<ID> tmpNewSafeVars(safevars); 00186 00187 bool reordered_aggregate = 00188 reorderForSafety(reg, tmpSrcBody, tmpTgt, tmpNewSafeVars); 00189 00190 if( tmpSrcBody.empty() ) { 00191 // fully consumed body -> make variables in aggregate body safe 00192 // TODO we did this before, but this should not be allowed! 00193 //safevars.insert(tmpNewSafeVars.begin(), tmpNewSafeVars.end()); 00194 00195 // if aggregate is an assignment and not in NAF, make assigned variable safe 00196 if( !it->isNaf() ) { 00197 if( atom.tuple[1] == ID::termFromBuiltin(ID::TERM_BUILTIN_EQ) ) { 00198 assert(atom.tuple[0] != ID_FAIL); 00199 if( atom.tuple[0].isVariableTerm() ) 00200 newsafevars.insert(atom.tuple[0]); 00201 } 00202 00203 if( atom.tuple[3] == ID::termFromBuiltin(ID::TERM_BUILTIN_EQ) ) { 00204 assert(atom.tuple[4] != ID_FAIL); 00205 if( atom.tuple[4].isVariableTerm() ) 00206 newsafevars.insert(atom.tuple[4]); 00207 } 00208 } 00209 00210 if( reordered_aggregate ) { 00211 LOG(WARNING,"TODO we should probably store back the reordered aggregate into the registry and transfer this new safety-reordered aggregate (and not propagate the reordering to the parent, as the parent does not change by reordering within the aggregate)"); 00212 } 00213 00214 transfer = true; 00215 } 00216 } 00217 else if( it->isBuiltinAtom() ) { 00218 const BuiltinAtom& atom = reg->batoms.getByID(*it); 00219 00220 // bailout-do 00221 do { 00222 // undocumented safety of dlv: 00223 // equality is safe in both directions 00224 // nothing else is safe in any direction for comparison builtins 00225 if( atom.tuple.size() == 3 && 00226 atom.tuple[0] == ID::termFromBuiltin(ID::TERM_BUILTIN_EQ) ) { 00227 DBGLOG(DBG," -> equality builtin"); 00228 if( !atom.tuple[1].isVariableTerm() || 00229 safevars.find(atom.tuple[1]) != safevars.end() ) { 00230 // first can make second safe 00231 if( atom.tuple[2].isVariableTerm() ) { 00232 // second is something that can be made safe 00233 newsafevars.insert(atom.tuple[2]); 00234 transfer = true; 00235 break; 00236 } 00237 else { 00238 transfer = true; 00239 break; 00240 } 00241 } 00242 if( !atom.tuple[2].isVariableTerm() || 00243 safevars.find(atom.tuple[2]) != safevars.end() ) { 00244 // second can make first safe 00245 if( atom.tuple[1].isVariableTerm() ) { 00246 // first is something that can be made safe 00247 newsafevars.insert(atom.tuple[1]); 00248 transfer = true; 00249 break; 00250 } 00251 else { 00252 transfer = true; 00253 break; 00254 } 00255 } 00256 } 00257 else if( isSafeBuiltinPredicate(static_cast<ID::TermBuiltinAddress>(atom.tuple[0].address)) ) { 00258 // safe if occurs as last variable in builtin predicate and other variables are safe 00259 // (see dlv manual) 00260 DBGLOG(DBG," -> 'safeBuiltinPredicate'"); 00261 Tuple::const_reverse_iterator itt = atom.tuple.rbegin(); 00262 // skip last one 00263 if( itt != atom.tuple.rend() ) 00264 itt++; 00265 bool good = true; 00266 for(; itt != atom.tuple.rend(); ++itt) { 00267 if( itt->isVariableTerm() && safevars.find(*itt) == safevars.end() ) { 00268 good = false; 00269 break; 00270 } 00271 } 00272 00273 if( good ) { 00274 if( atom.tuple.back().isVariableTerm() ) 00275 newsafevars.insert(atom.tuple.back()); 00276 transfer = true; 00277 break; 00278 } 00279 } 00280 else { 00281 // other -> all variables must be safe 00282 DBGLOG(DBG," -> other builtin"); 00283 bool good = true; 00284 BOOST_FOREACH(ID idv, atom.tuple) { 00285 if( idv.isVariableTerm() && 00286 safevars.find(idv) == safevars.end() ) { 00287 good = false; 00288 break; 00289 } 00290 } 00291 if( good ) 00292 transfer = true; 00293 } 00294 } 00295 while(false); // bailout-do 00296 } 00297 else { 00298 assert(false && "encountered unexpected literal during safety reordering"); 00299 } 00300 00301 if( transfer ) { 00302 // transfer it 00303 DBGLOG(DBG," -> transferring"); 00304 if( nottransferred ) 00305 reordered = true; 00306 tgt.push_back(*it); 00307 it = src.erase(it); 00308 } 00309 else { 00310 // do not transfer it 00311 DBGLOG(DBG," -> not transferring"); 00312 nottransferred = true; 00313 it++; 00314 } 00315 } 00316 00317 DBGLOG(DBG, "transferSafeLiteralsAndNewlySafeVariables returning with " 00318 "reordered=" << reordered << " and nottransferred=" << nottransferred); 00319 return reordered; 00320 } 00321 00322 bool reorderForSafety(RegistryPtr reg, std::list<ID>& src, Tuple& tgt, std::set<ID>& safevars) { 00323 DBGLOG_SCOPE(DBG, "rFS", false); 00324 DBGLOG(DBG, "=reorderForSafety"); 00325 assert(!!reg); 00326 assert(!src.empty()); 00327 00328 bool changed = false; 00329 do { 00330 DBGLOG(DBG, "safety reordering loop:"); 00331 DBGLOG(DBG, " src '" << 00332 printManyToString<RawPrinter>(Tuple(src.begin(), src.end()), ",", reg) << "'"); 00333 DBGLOG(DBG, " safevars '" << 00334 printManyToString<RawPrinter>(Tuple(safevars.begin(), safevars.end()), ",", reg) << "'"); 00335 00336 // 2.1) and 2.2) 00337 std::set<ID> newsafevars; 00338 changed |= transferSafeLiteralsAndNewlySafeVariables( 00339 reg, src, tgt, safevars, newsafevars); 00340 00341 DBGLOG(DBG, " -> src '" << 00342 printManyToString<RawPrinter>(Tuple(src.begin(), src.end()), ",", reg) << "'"); 00343 DBGLOG(DBG, " -> tgt '" << 00344 printManyToString<RawPrinter>(Tuple(tgt.begin(), tgt.end()), ",", reg) << "'"); 00345 DBGLOG(DBG, " -> newsafevars '" << 00346 printManyToString<RawPrinter>(Tuple(newsafevars.begin(), newsafevars.end()), ",", reg) << "'"); 00347 00348 safevars.insert(newsafevars.begin(), newsafevars.end()); 00349 00350 if( newsafevars.empty() ) 00351 break; 00352 } 00353 while( !src.empty() ); 00354 00355 return changed; 00356 } 00357 00358 } // anonymous namespace 00359 00360 00361 void 00362 SafetyChecker::operator() () const throw (SyntaxError) 00363 { 00364 Tuple unsafe = checkSafety(true); 00365 assert(unsafe.size() == 0); 00366 } 00367 00368 00369 Tuple 00370 SafetyChecker::checkSafety (bool throwOnUnsafeVariables) const throw (SyntaxError) 00371 { 00372 LOG_SCOPE(ANALYZE,"safety",false); 00373 LOG(ANALYZE,"=safety checker"); 00374 00375 RegistryPtr reg = ctx.registry(); 00376 assert(!!reg); 00377 00378 // 00379 // testing for simple rule safety: 00380 // * a constant is safe 00381 // * Note: for compatibility with other solvers, we do not assume 00382 // (as dlv does) that all anonymous variables are automatically safe 00383 // * a variable is safe if it occurs in a positive ordinary atom 00384 // * a variable is safe if it occurs as particular terms of a positive 00385 // builtin atom and particular other terms of that builtin atom are safe 00386 // (since 2010 dlv version the definition of "particular", below called 00387 // "certain", changed, and possibly will change again in the future) 00388 // * a variable is safe if it occurs in the output list of a positive 00389 // external atom and all input variables of that atom are safe 00390 // * a variable is safe if it occurs on one side of an assignment aggregate 00391 // * a variable is safe if it occurs in the positive body of an aggregate atom 00392 WARNING("the last line is a simplification") 00393 WARNING("see dlv documentation for better aggregate safety checking") 00394 // 00395 // algorithm (this algorithm does reordering for safety): 00396 // 1) init empty target rule body, init empty safe variables list 00397 // 2) do 00398 // 2.1) find all literals L={L1,L2,...} in source body which are safe 00399 // a) negative {ordinary atoms, external atoms, builtins, aggregates} with all variables safe 00400 // b) positive ordinary atoms 00401 // c) positive external atoms with all input variables safe 00402 // d) positive builtins with all "builtin input variables" safe 00403 // e) positive aggregates with assigned variables safe 00404 // 2.2) remove L from source body and append to target body, mark variables as safe 00405 // for b) mark all variables 00406 // for c) mark output variables 00407 // for d) mark "builtin output variables" 00408 // for e) mark assigned variables 00409 // 2.3) if source rule body not empty and variables were marked repeat 00410 // 3) if source rule body is empty and rule head contains only safe variables 00411 // 3.1) report rule as safe and store it back to registry 00412 // 3.2) otherwise throw an exception containing rule and variables 00413 00414 BOOST_FOREACH(ID idrule, ctx.idb) { 00415 DBGLOG(ANALYZE,"= check safety of rule " << 00416 printToString<RawPrinter>(idrule, reg)); 00417 00418 const Rule& rule = reg->rules.getByID(idrule); 00419 00420 // create rule with same kind and same storage space 00421 Rule newRule(rule.kind); 00422 std::set<ID> safevars; 00423 newRule.body.reserve(rule.body.size()); 00424 bool changed = false; 00425 00426 // only check body if not empty (not for disjunctive facts) 00427 if( !rule.body.empty() ) { 00428 // store source body in linked list (more efficient to modify) 00429 std::list<ID> src(rule.body.begin(), rule.body.end()); 00430 00431 // 2) 00432 changed = reorderForSafety(reg, src, newRule.body, safevars); 00433 00434 // 3) 00435 if( !src.empty() ) { 00436 // body is not safe -> report unsafe 00437 00438 // get body variables 00439 std::set<ID> remainingbodyvars; 00440 Tuple remainingbody(src.begin(), src.end()); 00441 reg->getVariablesInTuple(remainingbody, remainingbodyvars); 00442 00443 // get unsafe head variables 00444 Tuple unsafeBodyVars; 00445 std::back_insert_iterator<Tuple> inserter(unsafeBodyVars); 00446 std::set_difference( 00447 remainingbodyvars.begin(), remainingbodyvars.end(), 00448 safevars.begin(), safevars.end(), 00449 inserter); 00450 00451 #ifndef NDEBUG 00452 std::stringstream ss; 00453 BOOST_FOREACH (ID var, unsafeBodyVars) { ss << printToString<RawPrinter>(var, reg) << " "; } 00454 DBGLOG(DBG, "Found the following set of unsafe body variables: " << ss.str()); 00455 #endif 00456 00457 if (!throwOnUnsafeVariables) return unsafeBodyVars; 00458 else throw SyntaxError("Rule not safe (body): " 00459 "'" + printToString<RawPrinter>(idrule, reg) + "': " 00460 "literals not safe: " + 00461 printManyToString<RawPrinter>(Tuple(src.begin(), src.end()), ", ", reg) + ", " 00462 "safe variables: " + 00463 printManyToString<RawPrinter>(Tuple(safevars.begin(), safevars.end()), ", ", reg) + ", " 00464 "unsafe variables: " + 00465 printManyToString<RawPrinter>(Tuple(unsafeBodyVars.begin(), unsafeBodyVars.end()), ", ", reg)); 00466 } 00467 } 00468 00469 // variables in the positive head guard are also safe 00470 if( !rule.headGuard.empty() ) { 00471 // get all head guard variables 00472 std::set<ID> safeheadguardvars; 00473 reg->getVariablesInTuple(rule.headGuard, safeheadguardvars); 00474 00475 // get positive head guard atoms 00476 std::list<ID> posheadguard; 00477 BOOST_FOREACH (ID id, rule.headGuard) { 00478 if (!id.isNaf()) posheadguard.push_back(id); 00479 } 00480 00481 if( !posheadguard.empty() ) { 00482 // get headGuard variables 00483 Tuple headGuard(posheadguard.begin(), posheadguard.end()); 00484 reg->getVariablesInTuple(rule.headGuard, safevars); 00485 } 00486 #ifndef NDEBUG 00487 std::stringstream ss; 00488 BOOST_FOREACH (ID var, safevars) { ss << printToString<RawPrinter>(var, reg) << " "; } 00489 DBGLOG(DBG, "Found the following set of safe variables: " << ss.str()); 00490 #endif 00491 00492 // get all head guard variables 00493 std::set<ID> headguardvars; 00494 Tuple headguard(rule.headGuard.begin(), rule.headGuard.end()); 00495 reg->getVariablesInTuple(headguard, headguardvars); 00496 00497 // get unsafe head guard variables 00498 Tuple unsafeHeadGuardVars; 00499 std::back_insert_iterator<Tuple> inserter(unsafeHeadGuardVars); 00500 std::set_difference( 00501 headguardvars.begin(), headguardvars.end(), 00502 safevars.begin(), safevars.end(), 00503 inserter); 00504 00505 if (!unsafeHeadGuardVars.empty()) { 00506 #ifndef NDEBUG 00507 std::stringstream ss; 00508 BOOST_FOREACH (ID var, unsafeHeadGuardVars) { ss << printToString<RawPrinter>(var, reg) << " "; } 00509 DBGLOG(DBG, "Found the following set of unsafe head guard variables: " << ss.str()); 00510 #endif 00511 if (!throwOnUnsafeVariables) return unsafeHeadGuardVars; 00512 else throw SyntaxError("Rule not safe (head guard): " 00513 "'" + printToString<RawPrinter>(idrule, reg) + "': " 00514 "literals not safe: " + 00515 printManyToString<RawPrinter>(Tuple(rule.headGuard.begin(), rule.headGuard.end()), ", ", reg) + ", " 00516 "safe variables: " + 00517 printManyToString<RawPrinter>(Tuple(safeheadguardvars.begin(), safeheadguardvars.end()), ", ", reg) + ", " 00518 "unsafe variables: " + 00519 printManyToString<RawPrinter>(Tuple(unsafeHeadGuardVars.begin(), unsafeHeadGuardVars.end()), ", ", reg)); 00520 } 00521 } 00522 00523 // if we are here the body is safe -> check head 00524 00525 // get head variables 00526 std::set<ID> headvars; 00527 reg->getVariablesInTuple(rule.head, headvars); 00528 00529 // get unsafe head variables 00530 Tuple unsafeHeadVars; 00531 std::back_insert_iterator<Tuple> inserter(unsafeHeadVars); 00532 std::set_difference( 00533 headvars.begin(), headvars.end(), 00534 safevars.begin(), safevars.end(), 00535 inserter); 00536 00537 // report unsafe if unsafe 00538 if( !unsafeHeadVars.empty() ) { 00539 #ifndef NDEBUG 00540 std::stringstream ss; 00541 BOOST_FOREACH (ID var, unsafeHeadVars) { ss << printToString<RawPrinter>(var, reg) << " "; } 00542 DBGLOG(DBG, "Found the following set of unsafe head variables: " << ss.str()); 00543 #endif 00544 if (!throwOnUnsafeVariables) return unsafeHeadVars; 00545 else throw SyntaxError("Rule not safe (head): " 00546 "'" + printToString<RawPrinter>(idrule, reg) + "': " 00547 "variables not safe: " + 00548 printManyToString<RawPrinter>(unsafeHeadVars, ", ", reg)); 00549 } 00550 00551 // if rule body was reordered for safety, store it back to rule table (i.e., change rule!) 00552 if( changed ) { 00553 DBGLOG(DBG,"storing back rule " << printToString<RawPrinter>(idrule, reg)); 00554 newRule.head = rule.head; 00555 if (reg->rules.getIDByElement(newRule) == ID_FAIL) { 00556 reg->rules.update(rule, newRule); 00557 } 00558 DBGLOG(DBG,"-> reordered rule " << printToString<RawPrinter>(idrule, reg)); 00559 assert(&rule == &(reg->rules.getByID(idrule))); 00560 } 00561 } 00562 return Tuple(); 00563 } 00564 00565 00566 StrongSafetyChecker::StrongSafetyChecker(const ProgramCtx& ctx): 00567 SafetyCheckerBase(ctx) 00568 { 00569 } 00570 00571 00572 StrongSafetyChecker::~StrongSafetyChecker() 00573 { 00574 } 00575 00576 00577 // testing for strong safety: 00578 // 00579 // A rule is strongly safe, if 00580 // * it is safe and 00581 // * if an external atom in the rule is part of a cycle, each variable in 00582 // its output list occurs in a positive atom in the body, which does not 00583 // belong to the cycle. 00584 // 00585 // this is implemented as 00586 // for each component c 00587 // A) check if any external atom has output variables, if no exit with success 00588 // B) get all rule heads in c 00589 // C) for each rule r in c 00590 // a) for each external atom e in the body of r 00591 // 1) for each output variable of e 00592 // if e is part of a positive body atom of r 00593 // and this positive body atom of r does not unify with any rule head in c 00594 // then e is safe 00595 // 2) if any output variable of e is not safe, rule r is not strongly safe 00596 void 00597 StrongSafetyChecker::operator() () const throw (SyntaxError) 00598 { 00599 LOG_SCOPE(ANALYZE,"strongsafety",false); 00600 LOG(ANALYZE,"=strong safety checker"); 00601 00602 RegistryPtr reg = ctx.registry(); 00603 assert(!!reg); 00604 00605 if (ctx.config.getOption("LiberalSafety")) { 00606 return; 00607 } 00608 00609 // at this point we may (and do) assume that all rules are safe 00610 00611 // we also assume that we have a component graph 00612 assert(!!ctx.compgraph); 00613 ComponentGraph::ComponentIterator itc, itc_end; 00614 for(boost::tie(itc, itc_end) = ctx.compgraph->getComponents(); 00615 itc != itc_end; ++itc) { 00616 const ComponentGraph::ComponentInfo& ci = ctx.compgraph->propsOf(*itc); 00617 00618 // ignore components without inner eatoms 00619 // (they are automatically strongly safe) 00620 if( ci.innerEatoms.empty() ) 00621 continue; 00622 00623 // check if any external atom has output variables 00624 bool needToCheck = false; 00625 BOOST_FOREACH(ID eaid, ci.innerEatoms) { 00626 const ExternalAtom& eatom = reg->eatoms.getByID(eaid); 00627 BOOST_FOREACH(ID otid, eatom.tuple) { 00628 if( reg->getVariablesInID(otid).size() > 0 ) { 00629 needToCheck = true; 00630 break; 00631 } 00632 } 00633 if( needToCheck ) 00634 break; 00635 } 00636 00637 if( !needToCheck ) { 00638 DBGLOG(DBG,"no need to check strong safety " 00639 "as there are no outputs in internal eatoms " << 00640 printManyToString<RawPrinter>(ci.innerEatoms, ", ", reg)); 00641 continue; 00642 } 00643 DBGLOG(DBG,"need to check strong safety in component " << *itc); 00644 00645 // get rule heads here 00646 // here we store the full atom IDs (we need to unify, the predicate is not sufficient) 00647 std::set<ID> headAtomIDs; 00648 // we only consider inner rules (constraints have no heads) 00649 BOOST_FOREACH(ID rid, ci.innerRules) { 00650 const Rule& rule = reg->rules.getByID(rid); 00651 00652 BOOST_FOREACH(ID hid, rule.head) { 00653 if( !hid.isOrdinaryAtom() ) { 00654 LOG(WARNING,"ignoring non-ordinary atom in rule head for strong safety checking: " << 00655 printToString<RawPrinter>(hid, reg)); 00656 continue; 00657 } 00658 headAtomIDs.insert(hid); 00659 } 00660 } 00661 00662 DBGLOG(DBG,"in component " << *itc << 00663 " got set of heads '" << 00664 printManyToString<RawPrinter>( 00665 Tuple(headAtomIDs.begin(), headAtomIDs.end()), ", ", reg) << "'"); 00666 00667 // now check output variables 00668 00669 // we again only consider inner rules (positive domain expansion feedback 00670 // cannot happen through constraints as they cannot generate symbols) 00671 BOOST_FOREACH(ID rid, ci.innerRules) { 00672 if( !rid.doesRuleContainExtatoms() ) { 00673 DBGLOG(DBG,"skipping strong safety check for rule " << 00674 printToString<RawPrinter>(rid, reg) << " (no external atoms)"); 00675 continue; 00676 } 00677 00678 const Rule& rule = reg->rules.getByID(rid); 00679 00680 DBGLOG(DBG,"now checking strong safety of rule " << 00681 printToString<RawPrinter>(rid, reg)); 00682 00683 // find all variable outputs in all eatoms in this rule's body 00684 std::set<ID> varsToCheck; 00685 BOOST_FOREACH(ID lid, rule.body) { 00686 if( !lid.isExternalAtom() ) 00687 continue; 00688 00689 const ExternalAtom& eatom = reg->eatoms.getByID(lid); 00690 BOOST_FOREACH(ID tid, eatom.tuple) { 00691 reg->getVariablesInID(tid, varsToCheck); 00692 } 00693 } 00694 00695 WARNING("variables that do not occur in any other atom should be automatically strongly safe...?") 00696 DBGLOG(DBG,"need to find component-external domain predicate " 00697 "for variables {" << printManyToString<RawPrinter>( 00698 Tuple(varsToCheck.begin(), varsToCheck.end()), ", ", reg) + "}"); 00699 00700 // for each variable: 00701 // if it is part of a positive body atom of r 00702 // and this positive body atom of r does not unify with any rule head in c 00703 // then e is safe 00704 BOOST_FOREACH(ID vid, varsToCheck) { 00705 // check strong safety of variable vid 00706 DBGLOG(DBG,"checking strong safety of variable " << 00707 printToString<RawPrinter>(vid,reg)); 00708 00709 bool variableSafe = false; 00710 BOOST_FOREACH(ID lid, rule.body) { 00711 // skip negative bodies 00712 if( lid.isNaf() ) 00713 continue; 00714 00715 // skip external atoms, 00716 // they could but cannot in general be assumed to limit the domain 00717 // (and that's the reason we need to check strong safety) 00718 if( lid.isExternalAtom() ) 00719 continue; 00720 00721 // skip non-ordinary atoms 00722 WARNING("can we use aggregates to limit the domain for strong safety?") 00723 WARNING("can we use builtin atoms to limit the domain for strong safety?") 00724 if( lid.isAggregateAtom() || 00725 lid.isBuiltinAtom() ) 00726 continue; 00727 00728 assert(lid.isOrdinaryAtom()); 00729 00730 // check if this body literal contains the variable 00731 // and does not unify with any head 00732 // (only then the variable is safe) 00733 bool containsVariable = false; 00734 const OrdinaryAtom& oatom = reg->lookupOrdinaryAtom(lid); 00735 assert(!oatom.tuple.empty()); 00736 BOOST_FOREACH (ID var, reg->getVariablesInTuple(oatom.tuple)) { 00737 if( var == vid ) { 00738 containsVariable = true; 00739 break; 00740 } 00741 } 00742 00743 if( !containsVariable ) { 00744 DBGLOG(DBG,"skipping body literal " << 00745 printToString<RawPrinter>(lid, reg) << 00746 " (does not contain variable)"); 00747 continue; 00748 } 00749 00750 // oatom 'oatom' was retrieved using ID 'lid' 00751 DBGLOG(DBG,"checking unifications of body literal " << 00752 printToString<RawPrinter>(lid, reg) << " with component rule heads"); 00753 bool doesNotUnify = true; 00754 BOOST_FOREACH(ID hid, headAtomIDs) { 00755 DBGLOG(DBG,"checking against " << 00756 printToString<RawPrinter>(hid, reg)); 00757 assert(hid.isOrdinaryAtom()); 00758 const OrdinaryAtom& hoatom = reg->lookupOrdinaryAtom(hid); 00759 if( oatom.unifiesWith(hoatom, reg) ) { 00760 DBGLOG(DBG,"unification successful " 00761 "-> literal does not limit the domain"); 00762 doesNotUnify = false; 00763 break; 00764 } 00765 } 00766 00767 if( doesNotUnify ) { 00768 DBGLOG(DBG, "variable safe!"); 00769 variableSafe = true; 00770 break; 00771 } 00772 } 00773 00774 if( !variableSafe ) { 00775 throw SyntaxError("Rule is not strongly safe! " 00776 " Variable " + printToString<RawPrinter>(vid,reg) + 00777 " fails strong safety check in rule " + 00778 printToString<RawPrinter>(rid, reg)); 00779 } 00780 } 00781 } 00782 } 00783 } 00784 00785 00786 DLVHEX_NAMESPACE_END 00787 00788 00789 // vim:expandtab:ts=4:sw=4: 00790 // mode: C++ 00791 // End: