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 00034 #ifdef HAVE_CONFIG_H 00035 #include "config.h" 00036 #endif // HAVE_CONFIG_H 00037 00038 #include "dlvhex2/BaseModelGenerator.h" 00039 #include "dlvhex2/UnfoundedSetChecker.h" 00040 #include "dlvhex2/Printer.h" 00041 #include "dlvhex2/ProgramCtx.h" 00042 #include "dlvhex2/Printer.h" 00043 #include "dlvhex2/Benchmarking.h" 00044 #include "dlvhex2/ClaspSolver.h" 00045 00046 #include <boost/graph/graph_traits.hpp> 00047 #include <boost/graph/adjacency_list.hpp> 00048 #include <boost/graph/breadth_first_search.hpp> 00049 #include <boost/graph/visitors.hpp> 00050 #include <boost/graph/strong_components.hpp> 00051 00052 #include <fstream> 00053 00054 DLVHEX_NAMESPACE_BEGIN 00055 00056 /* 00057 * UnfoundedSetChecker 00058 * Base class for all unfounded set checkers 00059 */ 00060 00061 UnfoundedSetChecker::UnfoundedSetChecker( 00062 ProgramCtx& ctx, 00063 const OrdinaryASPProgram& groundProgram, 00064 InterpretationConstPtr componentAtoms, 00065 SimpleNogoodContainerPtr ngc) 00066 : mg(0), 00067 ctx(ctx), 00068 groundProgram(groundProgram), 00069 agp(emptyagp), 00070 componentAtoms(componentAtoms), 00071 ngc(ngc), 00072 domain(new Interpretation(ctx.registry())) 00073 { 00074 00075 reg = ctx.registry(); 00076 00077 mode = Ordinary; 00078 DBGLOG(DBG, "Starting UFS checker in ordinary mode, program idb has size " << groundProgram.idb.size()); 00079 } 00080 00081 00082 UnfoundedSetChecker::UnfoundedSetChecker( 00083 BaseModelGenerator* mg, 00084 ProgramCtx& ctx, 00085 const OrdinaryASPProgram& groundProgram, 00086 const AnnotatedGroundProgram& agp, 00087 InterpretationConstPtr componentAtoms, 00088 SimpleNogoodContainerPtr ngc) 00089 : mg(mg), 00090 ctx(ctx), 00091 groundProgram(groundProgram), 00092 agp(agp), 00093 componentAtoms(componentAtoms), 00094 ngc(ngc), 00095 domain(new Interpretation(ctx.registry())) 00096 { 00097 00098 reg = ctx.registry(); 00099 00100 mode = WithExt; 00101 DBGLOG(DBG, "Starting UFS checker in external atom mode, program idb has size " << groundProgram.idb.size()); 00102 } 00103 00104 00105 bool UnfoundedSetChecker::isUnfoundedSet(InterpretationConstPtr compatibleSet, InterpretationConstPtr compatibleSetWithoutAux, InterpretationConstPtr ufsCandidate) 00106 { 00107 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidisufs, "UnfoundedSetChecker::isUFS"); 00108 00109 // in debug mode we might want to do both checks (traditional and support set based) 00110 #ifndef NDEBUG 00111 // #define DOBOTHCHECKS 00112 #endif 00113 00114 // ordinary mode generates only real unfounded sets, hence there is no check required 00115 assert(mode == WithExt); 00116 00117 DBGLOG(DBG, "Checking if " << *ufsCandidate << " is an unfounded set"); 00118 00119 // build indices 00120 UnfoundedSetVerificationStatus ufsVerStatus(agp, domain, ufsCandidate, compatibleSet, compatibleSetWithoutAux); 00121 00122 // For check using support sets 00123 InterpretationPtr supportSetVerification; 00124 InterpretationPtr auxToVerify; 00125 if (ctx.config.getOption("SupportSets")) { 00126 // take external atom values from the ufsCandidate and ordinary atoms from I \ U 00127 DBGLOG(DBG, "Constructing interpretation for external atom evaluation from " << *ufsCandidate); 00128 supportSetVerification = InterpretationPtr(new Interpretation(reg)); 00129 auxToVerify = InterpretationPtr(new Interpretation(reg)); 00130 supportSetVerification->getStorage() = (compatibleSetWithoutAux->getStorage() - ufsCandidate->getStorage()); 00131 BOOST_FOREACH (IDAddress adr, ufsVerStatus.auxiliariesToVerify) if (ufsCandidate->getFact(adr)) supportSetVerification->setFact(adr); 00132 BOOST_FOREACH (IDAddress adr, ufsVerStatus.auxiliariesToVerify) auxToVerify->setFact(adr); 00133 } 00134 00135 // now evaluate one external atom after the other and check if the new truth value of the auxiliaries are justified 00136 #ifndef NDEBUG 00137 DBGLOG(DBG, "Verifying external atoms in UFS candidate"); 00138 int evalCnt = 0; 00139 #endif 00140 00141 bool isUFS = true; 00142 for (uint32_t eaIndex = 0; eaIndex < agp.getIndexedEAtoms().size(); ++eaIndex) { 00143 ID eaID = agp.getIndexedEAtom(eaIndex); 00144 00145 // we only evaluate external atoms which are relevant for some auxiliaries 00146 if (eaID.address >= ufsVerStatus.externalAtomAddressToAuxIndices.size() || 00147 ufsVerStatus.externalAtomAddressToAuxIndices[eaID.address].size() == 0) continue; 00148 const ExternalAtom& eatom = reg->eatoms.getByID(eaID); 00149 00150 if (ctx.config.getOption("SupportSets") && 00151 (eatom.getExtSourceProperties().providesCompletePositiveSupportSets() || eatom.getExtSourceProperties().providesCompleteNegativeSupportSets()) && 00152 agp.allowsForVerificationUsingCompleteSupportSets()) { 00153 DBGLOG(DBG, "Verifying " << eaID << " for UFS verification using complete support sets (" << *supportSetVerification << ")"); 00154 if (!agp.verifyExternalAtomsUsingCompleteSupportSets(eaIndex, supportSetVerification, auxToVerify)) { 00155 isUFS = false; 00156 // if we should do both checks, then remember the result and continue with the explicit check, 00157 // otherwise we already know the result 00158 #ifndef DOBOTHCHECKS 00159 break; 00160 #endif 00161 } 00162 } 00163 #ifndef DOBOTHCHECKS 00164 else 00165 #else 00166 bool suppSetResult = isUFS; 00167 #endif 00168 { 00169 #ifndef NDEBUG 00170 evalCnt++; 00171 #endif 00172 // update the indices 00173 if (!verifyExternalAtomByEvaluation(eaID, ufsCandidate, compatibleSet, ufsVerStatus)) { 00174 #ifdef DOBOTHCHECKS 00175 // if we did already a support set-based check, assert that it also failed 00176 assert((!ctx.config.getOption("SupportSets") || isUFS == suppSetResult) && 00177 "Explicit and support set approach for UFS checking gave different answers"); 00178 #endif 00179 isUFS = false; 00180 break; 00181 } 00182 00183 } 00184 } 00185 00186 DBGLOG(DBG, "Evaluated " << agp.getIndexedEAtoms().size() << " of " << agp.getIndexedEAtoms().size() << " external atoms"); 00187 00188 DBGLOG(DBG, "Candidate is " << (isUFS ? "" : "not ") << "an unfounded set (" << *ufsCandidate << ")"); 00189 return isUFS; 00190 } 00191 00192 00193 UnfoundedSetChecker::UnfoundedSetVerificationStatus::UnfoundedSetVerificationStatus( 00194 const AnnotatedGroundProgram& agp, 00195 InterpretationConstPtr domain, InterpretationConstPtr ufsCandidate, InterpretationConstPtr compatibleSet, InterpretationConstPtr compatibleSetWithoutAux 00196 ) 00197 { 00198 00199 assert (ufsCandidate->getRegistry() == compatibleSet->getRegistry() && compatibleSet->getRegistry() == compatibleSetWithoutAux->getRegistry()); 00200 00201 // get all pairs of mappings of auxiliaries to external atoms 00202 BOOST_FOREACH (AnnotatedGroundProgram::AuxToExternalAtoms auxToEA, agp.getAuxToEA()) { 00203 00204 // Check if this auxiliary needs to be verified; this is only the case if its truth value has changes over compatibleSet. 00205 // Note: If the truth value has not changed, then the guess could still be wrong. 00206 // But it has been proven in the optimization part of 00207 // "Efficient HEX-Program Evaluation based on Unfounded Sets" (Eiter, Fink, Krennwallner, Redl, Schller, JAIR, 2014) 00208 // that in such cases this wrong guess is irrelevant for the unfounded set, i.e., even with a correct guess the interpretation would still be unfounded. 00209 IDAddress aux = auxToEA.first; 00210 if (ufsCandidate->getFact(aux) != compatibleSet->getFact(aux)) { 00211 if (domain->getFact(aux) && compatibleSet->getRegistry()->ogatoms.getIDByAddress(aux).isExternalAuxiliary()) { 00212 auxiliariesToVerify.push_back(aux); 00213 auxIndexToRemainingExternalAtoms.push_back(std::set<ID>(agp.getAuxToEA(aux).begin(), agp.getAuxToEA(aux).end())); 00214 BOOST_FOREACH (ID eaID, agp.getAuxToEA(aux)) { 00215 while (externalAtomAddressToAuxIndices.size() <= eaID.address) externalAtomAddressToAuxIndices.push_back(std::vector<int>()); 00216 externalAtomAddressToAuxIndices[eaID.address].push_back(auxIndexToRemainingExternalAtoms.size() - 1); 00217 } 00218 } 00219 } 00220 } 00221 00222 // For check using explicit evaluation of external atoms: prepare input to external atom evaluations 00223 // Construct: compatibleSetWithoutAux - ufsCandidate 00224 // Remove the UFS from the compatible set, but do not remove EA auxiliaries 00225 // This does not hurt because EA auxiliaries can never be in the input to an external atom, 00226 // but keeping them has the advantage that negative learning is more effective 00227 DBGLOG(DBG, "Constructing input interpretation for external atom evaluation"); 00228 eaInput = InterpretationPtr(new Interpretation(compatibleSet->getRegistry())); 00229 eaInput->getStorage() = (compatibleSet->getStorage() - (ufsCandidate->getStorage() & compatibleSetWithoutAux->getStorage())); 00230 } 00231 00232 00233 // 1. evaluates an external atom and possibly learns during this step 00234 // 2. tries to verify its auxiliaries and returns the result of this trial 00235 // 3. updates the data structures used for unfounded set candidate verification 00236 bool UnfoundedSetChecker::verifyExternalAtomByEvaluation( 00237 ID eaID, // external atom 00238 // actual input to the check 00239 InterpretationConstPtr ufsCandidate, InterpretationConstPtr compatibleSet, 00240 // indices 00241 UnfoundedSetVerificationStatus& ufsVerStatus 00242 ) 00243 { 00244 if (ctx.config.getOption("ExternalAtomVerificationFromLearnedNogoods")) { 00245 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sideav, "UFS checker verifyEAtom by eav (attempt)"); 00246 BOOST_FOREACH (IDAddress adr, ufsVerStatus.auxiliariesToVerify){ 00247 ufsVerStatus.eaInput->setFact(ufsCandidate->getFact(adr)); 00248 } 00249 InterpretationConstPtr verifiedAuxes = eavTree.getVerifiedAuxiliaries(ufsVerStatus.eaInput, InterpretationConstPtr(), ctx.registry()); 00250 00251 // check if all auxes are verified 00252 bool verified = true; 00253 BOOST_FOREACH (IDAddress adr, ufsVerStatus.auxiliariesToVerify){ 00254 if (!verifiedAuxes->getFact(adr)) { 00255 verified = false; 00256 break; 00257 } 00258 } 00259 if (verified) { 00260 DBGLOG(DBG, "UFS checker: verified external atom without evaluation"); 00261 DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sideavs, "UFS checker verifyEAtom by eav (succeed)", 1); 00262 return true; 00263 }else{ 00264 DBGLOG(DBG, "UFS checker: could not verify external atom without evaluation --> will evaluate"); 00265 } 00266 } 00267 00268 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sideval, "UFS checker verify by eval"); 00269 00270 // evaluate 00271 DBGLOG(DBG, "Evaluate " << eaID << " for UFS verification " << (!!ngc ? "with" : "without") << " learning under " << *ufsVerStatus.eaInput); 00272 00273 const ExternalAtom& eatom = reg->eatoms.getByID(eaID); 00274 00275 // prepare answer interpretation 00276 InterpretationPtr eaResult = InterpretationPtr(new Interpretation(reg)); 00277 BaseModelGenerator::IntegrateExternalAnswerIntoInterpretationCB cb(eaResult); 00278 00279 if (!!ngc && !!solver) { 00280 // evaluate the external atom with learned, and add the learned nogoods in transformed form to the UFS detection problem 00281 int oldNogoodCount = ngc->getNogoodCount(); 00282 mg->evaluateExternalAtom(ctx, eaID, ufsVerStatus.eaInput, cb, ngc); 00283 DBGLOG(DBG, "O: Adding new valid input-output relationships from nogood container"); 00284 for (int i = oldNogoodCount; i < ngc->getNogoodCount(); ++i) { 00285 00286 const Nogood& ng = ngc->getNogood(i); 00287 if (ng.isGround()) { 00288 DBGLOG(DBG, "Processing learned nogood " << ng.getStringRepresentation(reg)); 00289 00290 std::pair<bool, Nogood> transformed = nogoodTransformation(ng, compatibleSet); 00291 if (transformed.first) { 00292 solver->addNogood(transformed.second); 00293 } 00294 00295 if (ctx.config.getOption("ExternalAtomVerificationFromLearnedNogoods")) { 00296 eavTree.addNogood(ng, reg, true); 00297 } 00298 } 00299 } 00300 } 00301 else { 00302 mg->evaluateExternalAtom(ctx, eaID, ufsVerStatus.eaInput, cb); 00303 } 00304 00305 // remove the external atom from the remaining lists of all auxiliaries which wait for the EA to be verified 00306 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidauxup, "UFS checker verify: aux up"); 00307 DBGLOG(DBG, "Updating data structures"); 00308 assert (eaID.address < ufsVerStatus.externalAtomAddressToAuxIndices.size()); 00309 BOOST_FOREACH (uint32_t i, ufsVerStatus.externalAtomAddressToAuxIndices[eaID.address]) { 00310 assert (i >= 0 && i < ufsVerStatus.auxIndexToRemainingExternalAtoms.size() && i < ufsVerStatus.auxiliariesToVerify.size()); 00311 DBGLOG(DBG, "Updating auxiliary " << ufsVerStatus.auxiliariesToVerify[i]); 00312 if (!ufsVerStatus.auxIndexToRemainingExternalAtoms[i].empty()) { 00313 ufsVerStatus.auxIndexToRemainingExternalAtoms[i].erase(eaID); 00314 00315 // if no external atoms remain to be verified, then the truth/falsity of the auxiliary is finally known 00316 if (ufsVerStatus.auxIndexToRemainingExternalAtoms[i].empty()) { 00317 // check if the auxiliary, which was assumed to be unfounded, is indeed _not_ in eaResult 00318 DBGLOG(DBG, "All relevant external atoms have been evaluated auxiliary, now checking if auxiliary " << ufsVerStatus.auxiliariesToVerify[i] << " (" << printToString<RawPrinter>(reg->ogatoms.getIDByAddress(ufsVerStatus.auxiliariesToVerify[i]), reg) << ") is justified"); 00319 DBGLOG(DBG, "Actual truth value: " << eaResult->getFact(ufsVerStatus.auxiliariesToVerify[i]) << ", assumed truth value under I u -X: " << ufsCandidate->getFact(ufsVerStatus.auxiliariesToVerify[i])); 00320 if (eaResult->getFact(ufsVerStatus.auxiliariesToVerify[i]) != ufsCandidate->getFact(ufsVerStatus.auxiliariesToVerify[i])) { 00321 00322 // wrong guess: the auxiliary is _not_ unfounded 00323 #ifndef NDEBUG 00324 DBGLOG(DBG, "Truth value of auxiliary " << ufsVerStatus.auxiliariesToVerify[i] << " is not justified --> Candidate is not an unfounded set"); 00325 DBGLOG(DBG, "Candidate is not an unfounded set (" << *ufsCandidate << ")"); 00326 #endif 00327 00328 return false; 00329 } 00330 else { 00331 DBGLOG(DBG, "Truth value of auxiliary " << ufsVerStatus.auxiliariesToVerify[i] << " is justified"); 00332 } 00333 } 00334 } 00335 } 00336 00337 return true; 00338 } 00339 00340 00341 Nogood UnfoundedSetChecker::getUFSNogood( 00342 const std::vector<IDAddress>& ufs, 00343 InterpretationConstPtr interpretation) 00344 { 00345 00346 #ifndef NDEBUG 00347 InterpretationPtr intr(new Interpretation(reg)); 00348 BOOST_FOREACH (IDAddress adr, ufs) { 00349 intr->setFact(adr); 00350 } 00351 DBGLOG(DBG, "Constructing nogoods for UFS: " << *intr); 00352 #endif 00353 00354 switch (ctx.config.getOption("UFSLearnStrategy")) { 00355 case 1: return getUFSNogoodReductBased(ufs, interpretation); 00356 case 2: return getUFSNogoodUFSBased(ufs, interpretation); 00357 default: throw GeneralError("Unknown UFSLern strategy"); 00358 } 00359 } 00360 00361 00362 Nogood UnfoundedSetChecker::getUFSNogoodReductBased( 00363 const std::vector<IDAddress>& ufs, 00364 InterpretationConstPtr interpretation) 00365 { 00366 00367 // reduct-based stratey 00368 Nogood ng; 00369 00370 #ifndef NDEBUG 00371 std::stringstream ss; 00372 bool first = true; 00373 ss << "{ "; 00374 BOOST_FOREACH (IDAddress adr, ufs) { 00375 ss << (!first ? ", " : "") << adr; 00376 first = false; 00377 } 00378 ss << " }"; 00379 DBGLOG(DBG, "Constructing UFS nogood for UFS " << ss.str() << " wrt. " << *interpretation); 00380 #endif 00381 00382 // for each rule with unsatisfied body 00383 BOOST_FOREACH (ID ruleId, groundProgram.idb) { 00384 const Rule& rule = reg->rules.getByID(ruleId); 00385 BOOST_FOREACH (ID b, rule.body) { 00386 if (interpretation->getFact(b.address) != !b.isNaf()) { 00387 // take an unsatisfied body literal 00388 ng.insert(NogoodContainer::createLiteral(b.address, interpretation->getFact(b.address))); 00389 break; 00390 } 00391 } 00392 } 00393 00394 // add the smaller FLP model (interpretation minus unfounded set), restricted to ordinary atoms 00395 InterpretationPtr smallerFLPModel = InterpretationPtr(new Interpretation(*interpretation)); 00396 BOOST_FOREACH (IDAddress adr, ufs) { 00397 smallerFLPModel->clearFact(adr); 00398 } 00399 bm::bvector<>::enumerator en = smallerFLPModel->getStorage().first(); 00400 bm::bvector<>::enumerator en_end = smallerFLPModel->getStorage().end(); 00401 while (en < en_end) { 00402 if (!reg->ogatoms.getIDByTuple(reg->ogatoms.getByAddress(*en).tuple).isAuxiliary()) { 00403 ng.insert(NogoodContainer::createLiteral(*en)); 00404 } 00405 en++; 00406 } 00407 00408 // add one atom which is in the original interpretation but not in the flp model 00409 en = interpretation->getStorage().first(); 00410 en_end = interpretation->getStorage().end(); 00411 while (en < en_end) { 00412 if (!smallerFLPModel->getFact(*en)) { 00413 ng.insert(NogoodContainer::createLiteral(*en)); 00414 break; 00415 } 00416 en++; 00417 } 00418 00419 DBGLOG(DBG, "Constructed UFS nogood " << ng); 00420 return ng; 00421 } 00422 00423 00424 Nogood UnfoundedSetChecker::getUFSNogoodUFSBased( 00425 const std::vector<IDAddress>& ufs, 00426 InterpretationConstPtr interpretation) 00427 { 00428 00429 // UFS-based strategy 00430 Nogood ng; 00431 00432 // take an atom from the unfounded set which is true in the interpretation 00433 DBGLOG(DBG, "Constructing UFS nogood"); 00434 BOOST_FOREACH (IDAddress adr, ufs) { 00435 if (interpretation->getFact(adr)) { 00436 ng.insert(NogoodContainer::createLiteral(adr, true)); 00437 break; 00438 } 00439 } 00440 00441 #ifndef NDEBUG 00442 int intersectionRules = 0; 00443 int nonExtRules = 0; 00444 #endif 00445 00446 // find all rules r such that H(r) intersects with the unfounded set 00447 BOOST_FOREACH (ID ruleID, groundProgram.idb) { 00448 const Rule& rule = reg->rules.getByID(ruleID); 00449 if (mg && (rule.isEAGuessingRule() /*|| (rule.head.size() == 1 && rule.head[0].isExternalAuxiliary())*/)) continue; 00450 00451 bool intersects = false; 00452 BOOST_FOREACH (ID h, rule.head) { 00453 if (std::find(ufs.begin(), ufs.end(), h.address) != ufs.end()) { 00454 intersects = true; 00455 break; 00456 } 00457 } 00458 if (!intersects) continue; 00459 #ifndef NDEBUG 00460 intersectionRules++; 00461 #endif 00462 00463 // Check if the rule is external ("external" to the UFS, has nothing to do with external atoms), i.e., it does _not_ contain an ordinary unfounded atom in its positive body 00464 // (if the rule is not external, then condition (ii) will always be satisfied wrt. this unfounded set) 00465 bool external = true; 00466 BOOST_FOREACH (ID b, rule.body) { 00467 if (interpretation->getFact(b.address) && !b.isNaf() && (!b.isExternalAuxiliary() || mode == Ordinary) && std::find(ufs.begin(), ufs.end(), b.address) != ufs.end()) { 00468 external = false; 00469 break; 00470 } 00471 } 00472 #ifndef NDEBUG 00473 if (external) { 00474 DBGLOG(DBG, "External rule: " << RawPrinter::toString(reg, ruleID)); 00475 } 00476 else { 00477 DBGLOG(DBG, "Non-external rule: " << RawPrinter::toString(reg, ruleID)); 00478 nonExtRules++; 00479 } 00480 #endif 00481 if (!external) continue; 00482 00483 // If available, find a literal which satisfies this rule independently of ufs; 00484 // this is either 00485 // (i) an ordinary body atom is false in the interpretation 00486 // (iii) a head atom, which is true in the interpretation and not in the unfounded set 00487 // because then the rule is no justification for the ufs 00488 bool foundInd = false; 00489 // (iii) 00490 BOOST_FOREACH (ID h, rule.head) { 00491 if (interpretation->getFact(h.address) && std::find(ufs.begin(), ufs.end(), h.address) == ufs.end()) { 00492 ng.insert(NogoodContainer::createLiteral(h.address, true)); 00493 DBGLOG(DBG, "Literal chosen by (iii)"); 00494 foundInd = true; 00495 break; 00496 } 00497 } 00498 if (!foundInd) { 00499 // (i) 00500 BOOST_FOREACH (ID b, rule.body) { 00501 if (!b.isNaf() != interpretation->getFact(b.address) && (!b.isExternalAuxiliary() || mode == Ordinary)) { 00502 ng.insert(NogoodContainer::createLiteral(b.address, false)); 00503 DBGLOG(DBG, "Literal chosen by (i)"); 00504 foundInd = true; 00505 break; 00506 } 00507 } 00508 } 00509 if (!foundInd) { 00510 // (ii) alternatively: collect the truth values of all atoms relevant to the external atoms in the rule body 00511 bool extFound = false; 00512 BOOST_FOREACH (ID b, rule.body) { 00513 if (mode == Ordinary || !b.isExternalAuxiliary()) { 00514 // this atom is satisfied by the interpretation (otherwise we had already foundInd), 00515 // therefore there must be another (external) atom which is false under I u -X 00516 // ng.insert(NogoodContainer::createLiteral(b.address, interpretation->getFact(b.address))); 00517 } 00518 else { 00519 assert(agp.mapsAux(b.address) && "mapping of auxiliary to EA not found"); 00520 const ExternalAtom& ea = reg->eatoms.getByID(agp.getAuxToEA(b.address)[0]); 00521 ea.updatePredicateInputMask(); 00522 bm::bvector<>::enumerator en = ea.getPredicateInputMask()->getStorage().first(); 00523 bm::bvector<>::enumerator en_end = ea.getPredicateInputMask()->getStorage().end(); 00524 while (en < en_end) { 00525 if (agp.getProgramMask()->getFact(*en) && domain->getFact(*en)) { 00526 // if (!ufs->getFact(*en)){ // atoms in the UFS will be always false under I u -X, thus their truth value in the interpretation is irrelevant 00527 ng.insert(NogoodContainer::createLiteral(*en, interpretation->getFact(*en))); 00528 // } 00529 } 00530 extFound = true; 00531 en++; 00532 } 00533 } 00534 } 00535 assert (extFound); 00536 DBGLOG(DBG, "Literal chosen by (ii)"); 00537 } 00538 } 00539 #ifndef NDEBUG 00540 DBGLOG(DBG, "During UFS nogood construction, " << intersectionRules << " of " << groundProgram.idb.size() << " rules intersected with the UFS and " << nonExtRules << " rules were non-external"); 00541 DBGLOG(DBG, "Constructed UFS nogood " << ng.getStringRepresentation(reg)); 00542 #endif 00543 00544 return ng; 00545 } 00546 00547 00548 /* 00549 * EncodingBasedUnfoundedSetChecker 00550 * 00551 * Encoding-based unfounded set checker 00552 * 00553 * The current assignment is used on the meta-level during construction of the UFS search problem. 00554 * This requires the re-construction of the UFS subproblem for each UFS check (if the assignment has changed). 00555 */ 00556 00557 EncodingBasedUnfoundedSetChecker::EncodingBasedUnfoundedSetChecker( 00558 ProgramCtx& ctx, 00559 const OrdinaryASPProgram& groundProgram, 00560 InterpretationConstPtr componentAtoms, 00561 SimpleNogoodContainerPtr ngc) : 00562 UnfoundedSetChecker(ctx, groundProgram, componentAtoms, ngc) 00563 { 00564 } 00565 00566 00567 EncodingBasedUnfoundedSetChecker::EncodingBasedUnfoundedSetChecker( 00568 BaseModelGenerator& mg, 00569 ProgramCtx& ctx, 00570 const OrdinaryASPProgram& groundProgram, 00571 const AnnotatedGroundProgram& agp, 00572 InterpretationConstPtr componentAtoms, 00573 SimpleNogoodContainerPtr ngc) : 00574 UnfoundedSetChecker(&mg, ctx, groundProgram, agp, componentAtoms, ngc) 00575 { 00576 } 00577 00578 00579 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblem( 00580 NogoodSet& ufsDetectionProblem, 00581 InterpretationConstPtr compatibleSet, 00582 InterpretationConstPtr compatibleSetWithoutAux, 00583 const std::set<ID>& skipProgram, 00584 std::vector<ID>& ufsProgram) 00585 { 00586 00587 int auxatomcnt = 0; 00588 constructUFSDetectionProblemNecessaryPart(ufsDetectionProblem, auxatomcnt, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 00589 constructUFSDetectionProblemOptimizationPart(ufsDetectionProblem, auxatomcnt, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 00590 } 00591 00592 00593 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblemNecessaryPart( 00594 NogoodSet& ufsDetectionProblem, 00595 int& auxatomcnt, 00596 InterpretationConstPtr compatibleSet, 00597 InterpretationConstPtr compatibleSetWithoutAux, 00598 const std::set<ID>& skipProgram, 00599 std::vector<ID>& ufsProgram) 00600 { 00601 00602 #ifndef NDEBUG 00603 std::stringstream programstring; 00604 RawPrinter printer(programstring, reg); 00605 #endif 00606 00607 DBGLOG(DBG, "Constructing necessary part of UFS detection problem"); 00608 DBGLOG(DBG, "N: Facts"); 00609 // facts cannot be in X 00610 if (groundProgram.edb) { 00611 bm::bvector<>::enumerator en = groundProgram.edb->getStorage().first(); 00612 bm::bvector<>::enumerator en_end = groundProgram.edb->getStorage().end(); 00613 while (en < en_end) { 00614 domain->setFact(*en); 00615 Nogood ng; 00616 ng.insert(NogoodContainer::createLiteral(*en, true)); 00617 ufsDetectionProblem.addNogood(ng); 00618 en++; 00619 } 00620 } 00621 00622 DBGLOG(DBG, "N: Rules"); 00623 BOOST_FOREACH (ID ruleID, ufsProgram) { 00624 00625 #ifndef NDEBUG 00626 programstring.str(""); 00627 printer.print(ruleID); 00628 DBGLOG(DBG, "Processing rule " << programstring.str()); 00629 #endif 00630 00631 const Rule& rule = reg->rules.getByID(ruleID); 00632 00633 if (ruleID.isWeightRule()) { 00634 // cycles through weight rules are not supported: the head atom must not be in the unfounded set 00635 if (ctx.config.getOption("AllowAggExtCycles")) { 00636 LOG(WARNING, "A cycle through weight rules was detected. This usually comes from cycles which involve both aggregates and external atoms and might result in non-minimal models. See aggregate options."); 00637 } 00638 else { 00639 throw GeneralError("A cycle through weight rules was detected. This usually comes from cycles which involve both aggregates and external atoms and is not allowed. See aggregate options."); 00640 } 00641 if (compatibleSet->getFact(rule.head[0].address)) { 00642 Nogood ng; 00643 ng.insert(NogoodContainer::createLiteral(rule.head[0].address, true)); 00644 ufsDetectionProblem.addNogood(ng); 00645 } 00646 continue; 00647 } 00648 00649 // condition 1 is handled directly: skip rules with unsatisfied body 00650 bool unsatisfied = false; 00651 BOOST_FOREACH (ID b, rule.body) { 00652 if (compatibleSet->getFact(b.address) != !b.isNaf()) { 00653 unsatisfied = true; 00654 break; 00655 } 00656 } 00657 if (unsatisfied) continue; 00658 00659 // Compute the set of problem variables: this is the set of all atoms which (1) occur in the head of some rule; or (2) are external atom auxiliaries 00660 BOOST_FOREACH (ID h, rule.head) domain->setFact(h.address); 00661 BOOST_FOREACH (ID b, rule.body) { 00662 domain->setFact(b.address); 00663 00664 if(mode == WithExt && b.isExternalAuxiliary()) { 00665 assert(agp.mapsAux(b.address) && "mapping of auxiliary to EA not found"); 00666 const ExternalAtom& ea = reg->eatoms.getByID(agp.getAuxToEA(b.address)[0]); 00667 ea.updatePredicateInputMask(); 00668 domain->getStorage() |= ea.getPredicateInputMask()->getStorage(); 00669 } 00670 } 00671 00672 // Create two unique predicates and atoms for this rule 00673 OrdinaryAtom hratom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 00674 hratom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, auxatomcnt++))); 00675 ID hr = reg->storeOrdinaryGAtom(hratom); 00676 00677 // hr is true iff one of the rule's head atoms is in X 00678 { 00679 Nogood ng; 00680 ng.insert(NogoodContainer::createLiteral(hr.address, true)); 00681 BOOST_FOREACH (ID h, rule.head) { 00682 ng.insert(NogoodContainer::createLiteral(h.address, false)); 00683 } 00684 ufsDetectionProblem.addNogood(ng); 00685 } 00686 { 00687 BOOST_FOREACH (ID h, rule.head) { 00688 Nogood ng; 00689 ng.insert(NogoodContainer::createLiteral(hr.address, false)); 00690 ng.insert(NogoodContainer::createLiteral(h.address, true)); 00691 ufsDetectionProblem.addNogood(ng); 00692 } 00693 } 00694 00695 { 00696 Nogood ng; 00697 // if hr is true, then it must not happen that neither Condition 2 nor Condition 3 is satisfied 00698 ng.insert(NogoodContainer::createLiteral(hr.address, true)); 00699 00700 // Condition 2: some body literal b, which is true in I, is false under I u -X 00701 // If b is ordinary (or considered to be ordinary), then this can only happen if b is positive because for a negative b, I \models b implies I u -X \models b 00702 // if b is external, then it can be either positive or negative because due to nonmonotonicity we might have I \models b but I u -X \not\models b (even if b is negative) 00703 // That is: It must not happen that 00704 // 1. all ordinary positive body atoms, which are true in I, are not in the unfounded set; and 00705 // 2. all external literals are true under I u -X 00706 BOOST_FOREACH (ID b, rule.body) { 00707 if (!b.isExternalAuxiliary() || mode == Ordinary) { 00708 // ordinary literal 00709 if (!b.isNaf() && compatibleSet->getFact(b.address)) { 00710 ng.insert(NogoodContainer::createLiteral(b.address, false)); 00711 } 00712 } 00713 else { 00714 // external literal 00715 ng.insert(NogoodContainer::createLiteral(b.address, !b.isNaf())); 00716 } 00717 } 00718 00719 // Condition 3: some head atom, which is true in I, is not in the unfounded set 00720 // That is: It must not happen, that all positive head atoms, which are true in I, are in the unfounded set (then the condition is not satisfied) 00721 BOOST_FOREACH (ID h, rule.head) { 00722 if (compatibleSet->getFact(h.address)) { 00723 ng.insert(NogoodContainer::createLiteral(h.address, true)); 00724 } 00725 } 00726 ufsDetectionProblem.addNogood(ng); 00727 } 00728 } 00729 00730 // we want a UFS which intersects (wrt. the domain) with I 00731 DBGLOG(DBG, "N: Intersection with I "); 00732 { 00733 Nogood ng; 00734 bm::bvector<>::enumerator en = compatibleSetWithoutAux->getStorage().first(); 00735 bm::bvector<>::enumerator en_end = compatibleSetWithoutAux->getStorage().end(); 00736 while (en < en_end) { 00737 if ((!componentAtoms || componentAtoms->getFact(*en)) && domain->getFact(*en)) { 00738 ng.insert(NogoodContainer::createLiteral(*en, false)); 00739 } 00740 en++; 00741 } 00742 ufsDetectionProblem.addNogood(ng); 00743 } 00744 00745 // the ufs must not contain a head atom of an ignored rule 00746 // (otherwise we cannot guarantee that the ufs remains an ufs for completed interpretations) 00747 DBGLOG(DBG, "N: Ignored rules"); 00748 { 00749 BOOST_FOREACH (ID ruleId, skipProgram) { 00750 const Rule& rule = reg->rules.getByID(ruleId); 00751 BOOST_FOREACH (ID h, rule.head) { 00752 Nogood ng; 00753 ng.insert(NogoodContainer::createLiteral(h.address, true)); 00754 ufsDetectionProblem.addNogood(ng); 00755 } 00756 } 00757 } 00758 00759 // the ufs must not contain an atom which is external to the component 00760 if (componentAtoms) { 00761 DBGLOG(DBG, "N: Restrict search to strongly connected component"); 00762 bm::bvector<>::enumerator en = domain->getStorage().first(); 00763 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 00764 while (en < en_end) { 00765 if ((!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) && !componentAtoms->getFact(*en)) { 00766 Nogood ng; 00767 ng.insert(NogoodContainer::createLiteral(*en, true)); 00768 ufsDetectionProblem.addNogood(ng); 00769 } 00770 en++; 00771 } 00772 } 00773 } 00774 00775 00776 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblemOptimizationPart( 00777 NogoodSet& ufsDetectionProblem, 00778 int& auxatomcnt, 00779 InterpretationConstPtr compatibleSet, 00780 InterpretationConstPtr compatibleSetWithoutAux, 00781 const std::set<ID>& skipProgram, 00782 std::vector<ID>& ufsProgram) 00783 { 00784 00785 DBGLOG(DBG, "Constructing optimization part of UFS detection problem"); 00786 constructUFSDetectionProblemOptimizationPartRestrictToCompatibleSet(ufsDetectionProblem, auxatomcnt, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 00787 if (mode == WithExt) { 00788 constructUFSDetectionProblemOptimizationPartBasicEAKnowledge(ufsDetectionProblem, auxatomcnt, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 00789 constructUFSDetectionProblemOptimizationPartLearnedFromMainSearch(ufsDetectionProblem, auxatomcnt, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 00790 00791 // use this optimization only if external learning is off; the two optimizations can influence each other and cause spurious contradictions 00792 if (!ngc) constructUFSDetectionProblemOptimizationPartEAEnforement(ufsDetectionProblem, auxatomcnt, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 00793 } 00794 } 00795 00796 00797 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblemOptimizationPartRestrictToCompatibleSet( 00798 NogoodSet& ufsDetectionProblem, 00799 int& auxatomcnt, 00800 InterpretationConstPtr compatibleSet, 00801 InterpretationConstPtr compatibleSetWithoutAux, 00802 const std::set<ID>& skipProgram, 00803 std::vector<ID>& ufsProgram) 00804 { 00805 00806 // ordinary atoms not in I must not be in the unfounded set 00807 DBGLOG(DBG, "O: Ordinary atoms not in I must not be in the unfounded set"); 00808 BOOST_FOREACH (ID ruleID, ufsProgram) { 00809 const Rule& rule = reg->rules.getByID(ruleID); 00810 BOOST_FOREACH (ID h, rule.head) { 00811 if (!compatibleSet->getFact(h.address)) { 00812 Nogood ng; 00813 ng.insert(NogoodContainer::createLiteral(h.address, true)); 00814 ufsDetectionProblem.addNogood(ng); 00815 } 00816 } 00817 BOOST_FOREACH (ID b, rule.body) { 00818 if ((!b.isExternalAuxiliary() || mode == Ordinary) && !compatibleSet->getFact(b.address)) { 00819 Nogood ng; 00820 ng.insert(NogoodContainer::createLiteral(b.address, true)); 00821 ufsDetectionProblem.addNogood(ng); 00822 } 00823 } 00824 } 00825 } 00826 00827 00828 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblemOptimizationPartBasicEAKnowledge( 00829 NogoodSet& ufsDetectionProblem, 00830 int& auxatomcnt, 00831 InterpretationConstPtr compatibleSet, 00832 InterpretationConstPtr compatibleSetWithoutAux, 00833 const std::set<ID>& skipProgram, 00834 std::vector<ID>& ufsProgram) 00835 { 00836 00837 // if none of the input atoms to an external atom, which are true in I, are in the unfounded set, then the truth value of the external atom cannot change 00838 DBGLOG(DBG, "O: Adding basic knowledge about external atom behavior"); 00839 for (uint32_t eaIndex = 0; eaIndex < agp.getIndexedEAtoms().size(); ++eaIndex) { 00840 const ExternalAtom& eatom = reg->eatoms.getByID(agp.getIndexedEAtom(eaIndex)); 00841 00842 eatom.updatePredicateInputMask(); 00843 00844 // if none of the input atoms, which are true in I, are unfounded, then the output of the external atom does not change 00845 Nogood inputNogood; 00846 bm::bvector<>::enumerator en = eatom.getPredicateInputMask()->getStorage().first(); 00847 bm::bvector<>::enumerator en_end = eatom.getPredicateInputMask()->getStorage().end(); 00848 while (en < en_end) { 00849 if (compatibleSet->getFact(*en)) { 00850 // T a \in I 00851 if ( !domain->getFact(*en) ) { 00852 // atom is true for sure in I u -X 00853 } 00854 else { 00855 // atom might be true in I u -X 00856 inputNogood.insert(NogoodContainer::createLiteral(*en, false)); 00857 } 00858 } 00859 else { 00860 // F a \in I 00861 if ( !domain->getFact(*en) ) { 00862 // atom is also false for sure in I u -X 00863 } 00864 } 00865 en++; 00866 } 00867 00868 // go through the output atoms 00869 agp.getEAMask(eaIndex)->updateMask(); 00870 en = agp.getEAMask(eaIndex)->mask()->getStorage().first(); 00871 en_end = agp.getEAMask(eaIndex)->mask()->getStorage().end(); 00872 while (en < en_end) { 00873 if (reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary()) { 00874 // do not extend the variable domain (this is counterproductive) 00875 if ( domain->getFact(*en) ) { 00876 Nogood ng = inputNogood; 00877 ng.insert(NogoodContainer::createLiteral(*en, !compatibleSet->getFact(*en))); 00878 ufsDetectionProblem.addNogood(ng); 00879 } 00880 } 00881 en++; 00882 } 00883 } 00884 } 00885 00886 00887 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblemOptimizationPartLearnedFromMainSearch( 00888 NogoodSet& ufsDetectionProblem, 00889 int& auxatomcnt, 00890 InterpretationConstPtr compatibleSet, 00891 InterpretationConstPtr compatibleSetWithoutAux, 00892 const std::set<ID>& skipProgram, 00893 std::vector<ID>& ufsProgram) 00894 { 00895 00896 // add the learned nogoods (in transformed form) 00897 if (!!ngc) { 00898 DBGLOG(DBG, "O: Adding valid input-output relationships from nogood container"); 00899 for (int i = 0; i < ngc->getNogoodCount(); ++i) { 00900 const Nogood& ng = ngc->getNogood(i); 00901 if (ng.isGround()) { 00902 DBGLOG(DBG, "Processing learned nogood " << ng.getStringRepresentation(reg)); 00903 00904 std::pair<bool, Nogood> transformed = nogoodTransformation(ng, compatibleSet); 00905 if (transformed.first) { 00906 ufsDetectionProblem.addNogood(transformed.second); 00907 } 00908 } 00909 } 00910 } 00911 } 00912 00913 00914 void EncodingBasedUnfoundedSetChecker::constructUFSDetectionProblemOptimizationPartEAEnforement( 00915 NogoodSet& ufsDetectionProblem, 00916 int& auxatomcnt, 00917 InterpretationConstPtr compatibleSet, 00918 InterpretationConstPtr compatibleSetWithoutAux, 00919 const std::set<ID>& skipProgram, 00920 std::vector<ID>& ufsProgram) 00921 { 00922 00923 // if there is no necessity to change the truth value of an external atom compared to compatibleSet, then do not do it 00924 // (this makes the postcheck cheaper) 00925 DBGLOG(DBG, "O: Enforcement of external atom truth values"); 00926 00927 // make aux('x', r) false iff one of B^{+}_o(r), which is true in compatibleSet, is true or one of H(r), which is true in compatibleSet, is false 00928 boost::unordered_map<ID, IDAddress> ruleToAux; 00929 BOOST_FOREACH (ID ruleID, ufsProgram) { 00930 const Rule& rule = reg->rules.getByID(ruleID); 00931 00932 OrdinaryAtom cratom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 00933 cratom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, auxatomcnt++))); 00934 ID cr = reg->storeOrdinaryGAtom(cratom); 00935 ruleToAux[ruleID] = cr.address; 00936 00937 // check if condition 1 applies for this rule 00938 bool condition1 = false; 00939 BOOST_FOREACH (ID b, rule.body) { 00940 if (compatibleSet->getFact(b.address) != !b.isNaf()) { 00941 // yes: set aux('x', r) to false 00942 condition1 = true; 00943 Nogood falsifyCr; 00944 falsifyCr.insert(NogoodContainer::createLiteral(cr.address, true)); 00945 ufsDetectionProblem.addNogood(falsifyCr); 00946 break; 00947 } 00948 } 00949 00950 if (!condition1) { 00951 Nogood ngnot; 00952 BOOST_FOREACH (ID b, rule.body) { 00953 if (!b.isNaf() && !b.isExternalAuxiliary() && compatibleSet->getFact(b.address)) { 00954 DBGLOG(DBG, "Binding positive body atom to c " << cr); 00955 Nogood ng; 00956 ng.insert(NogoodContainer::createLiteral(cr.address, true)); 00957 ng.insert(NogoodContainer::createLiteral(b.address, true)); 00958 ufsDetectionProblem.addNogood(ng); 00959 00960 ngnot.insert(NogoodContainer::createLiteral(b.address, false)); 00961 } 00962 } 00963 BOOST_FOREACH (ID h, rule.head) { 00964 if (compatibleSet->getFact(h.address)) { 00965 DBGLOG(DBG, "Binding head atom to c " << cr); 00966 Nogood ng; 00967 ng.insert(NogoodContainer::createLiteral(cr.address, true)); 00968 ng.insert(NogoodContainer::createLiteral(h.address, false)); 00969 ufsDetectionProblem.addNogood(ng); 00970 00971 ngnot.insert(NogoodContainer::createLiteral(h.address, true)); 00972 } 00973 } 00974 DBGLOG(DBG, "Negated nogood for c " << cr); 00975 ngnot.insert(NogoodContainer::createLiteral(cr.address, false)); 00976 ufsDetectionProblem.addNogood(ngnot); 00977 } 00978 } 00979 00980 // for all external atom auxiliaries 00981 std::set<IDAddress> eaAuxes; 00982 boost::unordered_map<IDAddress, std::vector<ID> > eaAuxToRule; 00983 00984 BOOST_FOREACH (ID ruleID, ufsProgram) { 00985 const Rule& rule = reg->rules.getByID(ruleID); 00986 BOOST_FOREACH (ID b, rule.body) { 00987 if (b.isExternalAuxiliary()) { 00988 eaAuxes.insert(b.address); 00989 eaAuxToRule[b.address].push_back(ruleID); 00990 } 00991 } 00992 } 00993 BOOST_FOREACH (IDAddress eaAux, eaAuxes) { 00994 // if all aux('x', r) are false for all rules where eaAux occurs ... 00995 Nogood ng; 00996 BOOST_FOREACH (ID ruleID, eaAuxToRule[eaAux]) { 00997 ng.insert(NogoodContainer::createLiteral(ruleToAux[ruleID], false)); 00998 } 00999 // then force aux to the same truth value as in compatibleSet 01000 ng.insert(NogoodContainer::createLiteral(eaAux, !compatibleSet->getFact(eaAux))); 01001 DBGLOG(DBG, "Enforcement of ea truth value"); 01002 ufsDetectionProblem.addNogood(ng); 01003 } 01004 } 01005 01006 01007 std::pair<bool, Nogood> EncodingBasedUnfoundedSetChecker::nogoodTransformation(Nogood ng, InterpretationConstPtr assignment) 01008 { 01009 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidtransform, "UFS nogood transformation"); 01010 01011 std::pair<bool, Nogood> ret; 01012 bool& valid = ret.first; valid = true; 01013 Nogood& ngAdd = ret.second; 01014 01015 BOOST_FOREACH (ID id, ng) { 01016 // we have to requery the ID because nogoods strip off unnecessary information (e.g. property flags) 01017 if (reg->ogatoms.getIDByAddress(id.address).isExternalAuxiliary()) { 01018 01019 ID useID = id; 01020 01021 // transform negative replacements to positive ones 01022 OrdinaryAtom ogatom = reg->ogatoms.getByID(id); 01023 if (ogatom.tuple[0] == reg->getAuxiliaryConstantSymbol('n', reg->getIDByAuxiliaryConstantSymbol(ogatom.tuple[0]))) { 01024 ogatom.tuple[0] = reg->getAuxiliaryConstantSymbol('r', reg->getIDByAuxiliaryConstantSymbol(ogatom.tuple[0])); 01025 useID = reg->storeOrdinaryGAtom(ogatom); 01026 // flip truth value 01027 useID.kind |= ID::NAF_MASK; 01028 } 01029 01030 // do not add a nogood if it extends the variable domain (this is counterproductive) 01031 if ( !domain->getFact(useID.address) ) { 01032 DBGLOG(DBG, "Skipping because " << useID.address << " expands the domain"); 01033 valid = false; 01034 break; 01035 } 01036 else { 01037 DBGLOG(DBG, "Inserting EA-Aux " << (useID.isNaf() ? "-" : "") << useID.address); 01038 ngAdd.insert(NogoodContainer::createLiteral(useID)); 01039 } 01040 } 01041 else { 01042 // input atom 01043 01044 // we have the following relations between sign S of the atom in the nogood, truth in assignment C and the unfounded set 01045 // S=positive, C=false --> nogood can never fire, skip it 01046 // S=positive, C=true --> nogood fires if the atom is NOT in the unfounded set (because it is not in the domain or it is false) 01047 // S=negative, C=true --> nogood fires if the atom IS in the unfounded set (because then it is false in I u -X) 01048 // S=negative, C=false --> nogood will always fire (wrt. this literal), skip the literal 01049 if (!id.isNaf()) { 01050 // positive 01051 if (assignment->getFact(id.address) == false) { 01052 // false in I --> nogood can never fire unter I u -X 01053 DBGLOG(DBG, "Skipping because " << id.address << " can never be true under I u -X"); 01054 valid = false; 01055 break; 01056 } 01057 else { 01058 // true in I --> nogood fires if X does not contain the atom 01059 if ( domain->getFact(id.address) ) { 01060 DBGLOG(DBG, "Inserting ordinary -" << id.address << " because it is true in I"); 01061 ngAdd.insert(NogoodContainer::createLiteral(id.address, false)); 01062 } 01063 else { 01064 DBGLOG(DBG, "Skipping ordinary " << id.address << " because it is not in the domain and can therefore never be in the unfounded set"); 01065 } 01066 } 01067 } 01068 else { 01069 // negative 01070 if (assignment->getFact(id.address) == true) { 01071 // positive variant is true in I --> nogood fires if it is also in X 01072 if ( !domain->getFact(id.address) ) { 01073 DBGLOG(DBG, "Skipping because " << id.address << " can never be false under I u -X"); 01074 valid = false; 01075 break; 01076 } 01077 else { 01078 DBGLOG(DBG, "Inserting " << id.address << " because it is false in I u -X if it is in X"); 01079 ngAdd.insert(NogoodContainer::createLiteral(id.address, true)); 01080 } 01081 } 01082 else { 01083 // positive variant is false in I --> it is also false in I u -X, skip literal 01084 DBGLOG(DBG, "Skipping ordinary -" << id.address << " because it is false in I and therefore also in I u -X"); 01085 } 01086 } 01087 } 01088 } 01089 DBGLOG(DBG, "Adding transformed nogood " << ngAdd << " (valid: " << valid << ")"); 01090 return ret; 01091 } 01092 01093 01094 void EncodingBasedUnfoundedSetChecker::learnNogoodsFromMainSearch(bool reset) 01095 { 01096 // nothing to do 01097 // (it is useless to learn nogoods now, because they will be forgetten anyway when the next UFS search is setup) 01098 } 01099 01100 01101 std::vector<IDAddress> EncodingBasedUnfoundedSetChecker::getUnfoundedSet(InterpretationConstPtr compatibleSet, const std::set<ID>& skipProgram) 01102 { 01103 01104 // remove external atom guessing rules and skipped rules from IDB 01105 std::vector<ID> ufsProgram; 01106 DBGLOG(DBG, "ch "); 01107 BOOST_FOREACH (ID ruleId, groundProgram.idb) { 01108 const Rule& rule = reg->rules.getByID(ruleId); 01109 if (mg && 01110 // EA-guessing rule 01111 (rule.isEAGuessingRule() || 01112 // ignored part of the program 01113 std::find(skipProgram.begin(), skipProgram.end(), ruleId) != skipProgram.end())) { 01114 // skip it 01115 } 01116 else { 01117 ufsProgram.push_back(ruleId); 01118 } 01119 } 01120 01121 // we need the the compatible set with and without auxiliaries 01122 InterpretationConstPtr compatibleSetWithoutAux = compatibleSet->getInterpretationWithoutExternalAtomAuxiliaries(); 01123 01124 #ifndef NDEBUG 01125 std::stringstream programstring; 01126 RawPrinter printer(programstring, reg); 01127 if (groundProgram.edb) programstring << "EDB: " << *groundProgram.edb << std::endl; 01128 programstring << "IDB:" << std::endl; 01129 BOOST_FOREACH (ID ruleId, ufsProgram) { 01130 printer.print(ruleId); 01131 programstring << std::endl; 01132 } 01133 DBGLOG(DBG, "Computing unfounded set of program:" << std::endl << programstring.str() << std::endl << "with respect to interpretation" << std::endl << *compatibleSetWithoutAux << " (" << *compatibleSet << ")"); 01134 #endif 01135 01136 // construct the UFS detection problem 01137 { 01138 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidcudp, "Construct UFS Detection Problem"); 01139 NogoodSet ufsDetectionProblem; 01140 constructUFSDetectionProblem(ufsDetectionProblem, compatibleSet, compatibleSetWithoutAux, skipProgram, ufsProgram); 01141 01142 // solve the ufs problem 01143 01144 // We need to freeze the variables in the domain since their truth values in the models are relevant. 01145 // However, since leaned clauses can only constraint frozen variables, 01146 // it seems to be better to freeze all. 01147 solver = SATSolver::getInstance(ctx, ufsDetectionProblem /*, domain*/); 01148 } 01149 InterpretationConstPtr model; 01150 01151 int mCnt = 0; 01152 01153 #ifdef DLVHEX_BENCHMARK 01154 DLVHEX_BENCHMARK_REGISTER(ufscheck, "UFS Check"); 01155 DLVHEX_BENCHMARK_REGISTER(oufscheck, "Ordinary UFS Check"); 01156 if( mode == WithExt ) { 01157 DLVHEX_BENCHMARK_START(ufscheck); 01158 } 01159 else { 01160 DLVHEX_BENCHMARK_START(oufscheck); 01161 } 01162 BOOST_SCOPE_EXIT( (ufscheck)(oufscheck)(mode) ) { 01163 if( mode == WithExt ) { 01164 DLVHEX_BENCHMARK_STOP(ufscheck); 01165 } 01166 else { 01167 DLVHEX_BENCHMARK_STOP(oufscheck); 01168 } 01169 } BOOST_SCOPE_EXIT_END 01170 #endif 01171 01172 DLVHEX_BENCHMARK_REGISTER(sidufsenum, "UFS-Detection Problem Solving"); 01173 if (mode == WithExt) { 01174 DLVHEX_BENCHMARK_START(sidufsenum); 01175 } 01176 model = solver->getNextModel(); 01177 if (mode == WithExt) { 01178 DLVHEX_BENCHMARK_STOP(sidufsenum); 01179 } 01180 while ( model != InterpretationConstPtr()) { 01181 if (mode == WithExt) { 01182 DLVHEX_BENCHMARK_REGISTER_AND_COUNT(ufscandidates, "Checked UFS candidates", 1); 01183 } 01184 01185 // check if the model is actually an unfounded set 01186 DBGLOG(DBG, "Got UFS candidate: " << *model); 01187 mCnt++; 01188 01189 if (mode == Ordinary || isUnfoundedSet(compatibleSet, compatibleSetWithoutAux, model)) { 01190 DBGLOG(DBG, "Found UFS: " << *model << " (interpretation: " << *compatibleSet << ")"); 01191 01192 std::vector<IDAddress> ufs; 01193 01194 bm::bvector<>::enumerator en = model->getStorage().first(); 01195 bm::bvector<>::enumerator en_end = model->getStorage().end(); 01196 while (en < en_end) { 01197 if (domain->getFact(*en)) ufs.push_back(*en); 01198 en++; 01199 } 01200 01201 DBGLOG(DBG, "Enumerated " << mCnt << " UFS candidates"); 01202 01203 solver.reset(); 01204 01205 if (mode == WithExt) { 01206 DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sidfailedufscheckcount, "Failed UFS Checks", 1); 01207 } 01208 01209 return ufs; 01210 } 01211 else { 01212 DBGLOG(DBG, "No UFS: " << *model); 01213 } 01214 01215 if (mode == WithExt) { 01216 DLVHEX_BENCHMARK_START(sidufsenum); 01217 } 01218 model = solver->getNextModel(); 01219 if (mode == WithExt) { 01220 DLVHEX_BENCHMARK_STOP(sidufsenum); 01221 } 01222 } 01223 01224 DBGLOG(DBG, "Enumerated " << mCnt << " UFS candidates"); 01225 solver.reset(); 01226 // no ufs 01227 std::vector<IDAddress> ufs; 01228 return ufs; 01229 } 01230 01231 01232 /* 01233 * AssumptionBasedUnfoundedSetChecker 01234 * 01235 * Assumption-based unfounded set checker 01236 * 01237 * The current assignment is used on the object-level (in the encoding) and can be inserted during the UFS check 01238 * by appropriate assumptions in the solver. 01239 * This allows for reusing of the UFS subproblem for each UFS check (even if the assignment has changed). 01240 */ 01241 01242 void AssumptionBasedUnfoundedSetChecker::constructDomain() 01243 { 01244 01245 // EDB 01246 if (groundProgram.edb) { 01247 bm::bvector<>::enumerator en = groundProgram.edb->getStorage().first(); 01248 bm::bvector<>::enumerator en_end = groundProgram.edb->getStorage().end(); 01249 while (en < en_end) { 01250 domain->setFact(*en); 01251 en++; 01252 } 01253 } 01254 01255 // IDB 01256 BOOST_FOREACH (ID ruleID, groundProgram.idb) { 01257 const Rule& rule = reg->rules.getByID(ruleID); 01258 if (mg && (rule.isEAGuessingRule() /*|| (rule.head.size() == 1 && rule.head[0].isExternalAuxiliary())*/)) continue; 01259 BOOST_FOREACH (ID h, rule.head) domain->setFact(h.address); 01260 BOOST_FOREACH (ID b, rule.body) { 01261 domain->setFact(b.address); 01262 01263 if(mode == WithExt && b.isExternalAuxiliary()) { 01264 assert(agp.mapsAux(b.address) && "mapping of auxiliary to EA not found"); 01265 const ExternalAtom& ea = reg->eatoms.getByID(agp.getAuxToEA(b.address)[0]); 01266 ea.updatePredicateInputMask(); 01267 domain->getStorage() |= ea.getPredicateInputMask()->getStorage(); 01268 } 01269 } 01270 } 01271 } 01272 01273 01274 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemFacts(NogoodSet& ufsDetectionProblem) 01275 { 01276 // facts cannot be in X 01277 DBGLOG(DBG, "N: Facts"); 01278 if (groundProgram.edb) { 01279 bm::bvector<>::enumerator en = groundProgram.edb->getStorage().first(); 01280 bm::bvector<>::enumerator en_end = groundProgram.edb->getStorage().end(); 01281 while (en < en_end) { 01282 Nogood ng; 01283 ng.insert(NogoodContainer::createLiteral(*en, true)); 01284 ufsDetectionProblem.addNogood(ng); 01285 en++; 01286 } 01287 } 01288 } 01289 01290 01291 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemCreateAuxAtoms() 01292 { 01293 01294 bm::bvector<>::enumerator en = domain->getStorage().first(); 01295 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01296 while (en < en_end) { 01297 OrdinaryAtom interpretationShadowAtom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01298 interpretationShadowAtom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01299 interpretationShadow[*en] = reg->storeOrdinaryGAtom(interpretationShadowAtom).address; 01300 01301 if (!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) { 01302 OrdinaryAtom residualShadowAtom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01303 residualShadowAtom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01304 residualShadow[*en] = reg->storeOrdinaryGAtom(residualShadowAtom).address; 01305 01306 OrdinaryAtom becomeFalseAtom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01307 becomeFalseAtom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01308 becomeFalse[*en] = reg->storeOrdinaryGAtom(becomeFalseAtom).address; 01309 01310 OrdinaryAtom aIandU(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01311 aIandU.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01312 IandU[*en] = reg->storeOrdinaryGAtom(aIandU).address; 01313 01314 OrdinaryAtom anIorU(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01315 anIorU.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01316 nIorU[*en] = reg->storeOrdinaryGAtom(anIorU).address; 01317 } 01318 01319 en++; 01320 } 01321 } 01322 01323 01324 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemRule(NogoodSet& ufsDetectionProblem, ID ruleID) 01325 { 01326 01327 const Rule& rule = reg->rules.getByID(ruleID); 01328 if (mg && (rule.isEAGuessingRule() /*|| (rule.head.size() == 1 && rule.head[0].isExternalAuxiliary())*/)) { 01329 DBGLOG(DBG, "Skipping rule " << printToString<RawPrinter>(ruleID, reg)); 01330 return; 01331 } 01332 01333 #ifndef NDEBUG 01334 std::stringstream programstring; 01335 RawPrinter printer(programstring, reg); 01336 printer.print(ruleID); 01337 DBGLOG(DBG, "Processing rule " << programstring.str()); 01338 #endif 01339 01340 if (ruleID.isWeightRule()) { 01341 // cycles through weight rules are not supported: the head atom must not be in the unfounded set 01342 if (ctx.config.getOption("AllowAggExtCycles")) { 01343 LOG(WARNING, "A cycle through weight rules was detected. This usually comes from cycles which involve both aggregates and external atoms and might result in non-minimal models. See aggregate options."); 01344 } 01345 else { 01346 throw GeneralError("A cycle through weight rules was detected. This usually comes from cycles which involve both aggregates and external atoms and is not allowed. See aggregate options."); 01347 } 01348 Nogood ng; 01349 // ng.insert(NogoodContainer::createLiteral(interpretationShadow[rule.head[0].address], true)); 01350 ng.insert(NogoodContainer::createLiteral(rule.head[0].address, true)); 01351 ufsDetectionProblem.addNogood(ng); 01352 return; 01353 } 01354 01355 // Create a unique predicate and atom h_r for this rule 01356 ID hr; 01357 { 01358 OrdinaryAtom hratom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01359 hratom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01360 hr = reg->storeOrdinaryGAtom(hratom); 01361 } 01362 01363 // hr is true iff one of the rule's head atoms is in X 01364 DBGLOG(DBG, "Binding hr to head atom"); 01365 { 01366 Nogood ng; 01367 ng.insert(NogoodContainer::createLiteral(hr.address, true)); 01368 BOOST_FOREACH (ID h, rule.head) { 01369 ng.insert(NogoodContainer::createLiteral(h.address, false)); 01370 } 01371 ufsDetectionProblem.addNogood(ng); 01372 } 01373 { 01374 BOOST_FOREACH (ID h, rule.head) { 01375 Nogood ng; 01376 ng.insert(NogoodContainer::createLiteral(hr.address, false)); 01377 ng.insert(NogoodContainer::createLiteral(h.address, true)); 01378 ufsDetectionProblem.addNogood(ng); 01379 } 01380 } 01381 01382 { 01383 Nogood ng; 01384 // if hr is true, then it must not happen that neither Condition 1 nor Condition 2 nor Condition 3 is satisfied 01385 ng.insert(NogoodContainer::createLiteral(hr.address, true)); 01386 01387 // Condition 1: some body literal b is unsatisfied by I 01388 // hence, it must not happen that all body literals are simultanously satisfied by I 01389 DBGLOG(DBG, "Condition 1"); 01390 BOOST_FOREACH (ID b, rule.body) { 01391 ng.insert(NogoodContainer::createLiteral(interpretationShadow[b.address], !b.isNaf())); 01392 } 01393 01394 // Condition 2: some body literal b, which is true in I, is false under I u -X 01395 // If b is ordinary (or considered to be ordinary), then this can only happen if b is positive because for a negative b, I \models b implies I u -X \models b 01396 // if b is external, then it can be either positive or negative because due to nonmonotonicity we might have I \models b but I u -X \not\models b (even if b is negative) 01397 // That is: It must not happen that 01398 // 1. all ordinary positive body atoms, which are true in I, are not in the unfounded set; and 01399 // 2. all external literals are true under I u -X 01400 DBGLOG(DBG, "Condition 2"); 01401 BOOST_FOREACH (ID b, rule.body) { 01402 if (!b.isExternalAuxiliary() || mode == Ordinary) { 01403 if (!b.isNaf()) { 01404 ng.insert(NogoodContainer::createLiteral(IandU[b.address], false)); 01405 } 01406 } 01407 else { 01408 // external literal 01409 ng.insert(NogoodContainer::createLiteral(b.address, !b.isNaf())); 01410 } 01411 } 01412 01413 // Condition 3: some head atom, which is true in I, is not in the unfounded set 01414 // That is: It must not happen, that all positive head atoms, which are true in I, are in the unfounded set (then the condition is not satisfied) 01415 DBGLOG(DBG, "Condition 3"); 01416 BOOST_FOREACH (ID h, rule.head) { 01417 ng.insert(NogoodContainer::createLiteral(nIorU[h.address], true)); 01418 } 01419 01420 DBGLOG(DBG, "Checking conditions 1, 2, 3"); 01421 ufsDetectionProblem.addNogood(ng); 01422 } 01423 } 01424 01425 01426 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemDefineAuxiliaries(NogoodSet& ufsDetectionProblem) 01427 { 01428 01429 DBGLOG(DBG, "N: Define residual shadow"); 01430 { 01431 bm::bvector<>::enumerator en = domain->getStorage().first(); 01432 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01433 while (en < en_end) { 01434 if (!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) { 01435 // define: residual shadow rs=true iff en is true in I u -X 01436 ID is = reg->ogatoms.getIDByAddress(interpretationShadow[*en]); 01437 ID rs = reg->ogatoms.getIDByAddress(residualShadow[*en]); 01438 01439 { 01440 Nogood ng1; 01441 ng1.insert(NogoodContainer::createLiteral(is.address, true)); 01442 ng1.insert(NogoodContainer::createLiteral(*en, false)); 01443 ng1.insert(NogoodContainer::createLiteral(rs.address, false)); 01444 ufsDetectionProblem.addNogood(ng1); 01445 } 01446 { 01447 Nogood ng2; 01448 ng2.insert(NogoodContainer::createLiteral(is.address, false)); 01449 ng2.insert(NogoodContainer::createLiteral(rs.address, true)); 01450 ufsDetectionProblem.addNogood(ng2); 01451 } 01452 { 01453 Nogood ng3; 01454 ng3.insert(NogoodContainer::createLiteral(*en, true)); 01455 ng3.insert(NogoodContainer::createLiteral(rs.address, true)); 01456 ufsDetectionProblem.addNogood(ng3); 01457 } 01458 } 01459 en++; 01460 } 01461 } 01462 01463 DBGLOG(DBG, "N: Define \"became false\""); 01464 { 01465 bm::bvector<>::enumerator en = domain->getStorage().first(); 01466 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01467 while (en < en_end) { 01468 if (!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) { 01469 // define: became false bf=true iff en is true in I and true in X 01470 ID is = reg->ogatoms.getIDByAddress(interpretationShadow[*en]); 01471 ID bf = reg->ogatoms.getIDByAddress(becomeFalse[*en]); 01472 01473 { 01474 Nogood ng1; 01475 ng1.insert(NogoodContainer::createLiteral(is.address, true)); 01476 ng1.insert(NogoodContainer::createLiteral(*en, true)); 01477 ng1.insert(NogoodContainer::createLiteral(bf.address, false)); 01478 ufsDetectionProblem.addNogood(ng1); 01479 } 01480 { 01481 Nogood ng2; 01482 ng2.insert(NogoodContainer::createLiteral(is.address, false)); 01483 ng2.insert(NogoodContainer::createLiteral(bf.address, true)); 01484 ufsDetectionProblem.addNogood(ng2); 01485 } 01486 { 01487 Nogood ng3; 01488 ng3.insert(NogoodContainer::createLiteral(*en, false)); 01489 ng3.insert(NogoodContainer::createLiteral(bf.address, true)); 01490 ufsDetectionProblem.addNogood(ng3); 01491 } 01492 } 01493 en++; 01494 } 01495 } 01496 01497 // for all ordinary atoms a 01498 // define: a_{IandU} := a_I \wedge a 01499 // define: a_{\overline{I}orU} := \neg a_I \or a 01500 DBGLOG(DBG, "N: Define a_{IandU} :- a_I \\wedge a and a_{\\overline{I}orU} :- \\neg a_I \\vee a"); 01501 { 01502 bm::bvector<>::enumerator en = domain->getStorage().first(); 01503 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01504 while (en < en_end) { 01505 if (!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) { 01506 ID is = reg->ogatoms.getIDByAddress(interpretationShadow[*en]); 01507 01508 // define: a_{IandU} := a_I \wedge a 01509 // we define a new atom a_{IandU}, which serves as a replacement for a, as follows: 01510 // 1. if a_I is false, then a_{IandU} is false 01511 // 2. if a is false, then a_{IandU} is false 01512 // 3. otherwise (a_I is true and a is true) a_{IandU} is true 01513 { 01514 IDAddress aIandU_IDA = IandU[*en]; 01515 01516 // 1. 01517 Nogood ng1; 01518 ng1.insert(NogoodContainer::createLiteral(is.address, false)); 01519 ng1.insert(NogoodContainer::createLiteral(aIandU_IDA, true)); 01520 ufsDetectionProblem.addNogood(ng1); 01521 01522 // 2. 01523 Nogood ng2; 01524 // ng2.insert(NogoodContainer::createLiteral(is.address, true)); 01525 ng2.insert(NogoodContainer::createLiteral(*en, false)); 01526 ng2.insert(NogoodContainer::createLiteral(aIandU_IDA, true)); 01527 ufsDetectionProblem.addNogood(ng2); 01528 01529 // 3. 01530 Nogood ng3; 01531 ng3.insert(NogoodContainer::createLiteral(is.address, true)); 01532 ng3.insert(NogoodContainer::createLiteral(*en, true)); 01533 ng3.insert(NogoodContainer::createLiteral(aIandU_IDA, false)); 01534 ufsDetectionProblem.addNogood(ng3); 01535 } 01536 01537 // define: a_{\overline{I}orU} := \neg a_I \or a 01538 // we define a new atom a_{\overline{I}orU}, which serves as a replacement for a, as follows: 01539 // 1. if a_I is false, then a_{\overline{I}orU} is true 01540 // 2. if a is true, then a_{\overline{I}orU} is true 01541 // 3. otherwise (a_I is true and a is false) a_{\overline{I}orU} is false 01542 { 01543 IDAddress anIorU_IDA = nIorU[*en]; 01544 01545 // 1. 01546 Nogood ng1; 01547 ng1.insert(NogoodContainer::createLiteral(is.address, false)); 01548 ng1.insert(NogoodContainer::createLiteral(anIorU_IDA, false)); 01549 ufsDetectionProblem.addNogood(ng1); 01550 01551 // 2. 01552 Nogood ng2; 01553 // ng2.insert(NogoodContainer::createLiteral(is.address, true)); 01554 ng2.insert(NogoodContainer::createLiteral(*en, true)); 01555 ng2.insert(NogoodContainer::createLiteral(anIorU_IDA, false)); 01556 ufsDetectionProblem.addNogood(ng2); 01557 01558 // 3. 01559 Nogood ng3; 01560 ng3.insert(NogoodContainer::createLiteral(is.address, true)); 01561 ng3.insert(NogoodContainer::createLiteral(*en, false)); 01562 ng3.insert(NogoodContainer::createLiteral(anIorU_IDA, true)); 01563 ufsDetectionProblem.addNogood(ng3); 01564 } 01565 } 01566 en++; 01567 } 01568 } 01569 } 01570 01571 01572 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemNonempty(NogoodSet& ufsDetectionProblem) 01573 { 01574 DBGLOG(DBG, "N: Nonempty"); 01575 Nogood ng; 01576 bm::bvector<>::enumerator en = domain->getStorage().first(); 01577 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01578 while (en < en_end) { 01579 if (!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) { 01580 ng.insert(NogoodContainer::createLiteral(*en, false)); 01581 } 01582 en++; 01583 } 01584 01585 // add the hook atom to allow for retracting this nogood later 01586 ng.insert(NogoodContainer::createLiteral(hookAtom.address, true)); 01587 01588 ufsDetectionProblem.addNogood(ng); 01589 } 01590 01591 01592 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemRestrictToSCC(NogoodSet& ufsDetectionProblem) 01593 { 01594 01595 if (componentAtoms) { 01596 DBGLOG(DBG, "N: Restrict search to strongly connected component"); 01597 bm::bvector<>::enumerator en = domain->getStorage().first(); 01598 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01599 while (en < en_end) { 01600 if ((!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) && !componentAtoms->getFact(*en)) { 01601 Nogood ng; 01602 ng.insert(NogoodContainer::createLiteral(*en, true)); 01603 01604 // add the hook atom to allow for retracting this nogood later 01605 ng.insert(NogoodContainer::createLiteral(hookAtom.address, true)); 01606 01607 ufsDetectionProblem.addNogood(ng); 01608 } 01609 en++; 01610 } 01611 } 01612 } 01613 01614 01615 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemBasicEABehavior(NogoodSet& ufsDetectionProblem) 01616 { 01617 01618 // if none of the input atoms to an external atom, which are true in I, are in the unfounded set, then the truth value of the external atom cannot change 01619 DBGLOG(DBG, "O: Adding basic knowledge about external atom behavior"); 01620 for (uint32_t eaIndex = 0; eaIndex < agp.getIndexedEAtoms().size(); ++eaIndex) { 01621 const ExternalAtom& eatom = reg->eatoms.getByID(agp.getIndexedEAtom(eaIndex)); 01622 01623 eatom.updatePredicateInputMask(); 01624 01625 // if none of the input atoms in the scope of this UFS checker, which are true in I, are unfounded, then the output of the external atom does not change 01626 Nogood inputNogood; 01627 bm::bvector<>::enumerator en = eatom.getPredicateInputMask()->getStorage().first(); 01628 bm::bvector<>::enumerator en_end = eatom.getPredicateInputMask()->getStorage().end(); 01629 while (en < en_end) { 01630 if (domain->getFact(*en)) inputNogood.insert(NogoodContainer::createLiteral(becomeFalse[*en], false)); 01631 en++; 01632 } 01633 // make sure that this nogood is invalidated over an extended domain 01634 inputNogood.insert(NogoodContainer::createLiteral(hookAtom.address, true)); 01635 01636 // go through the output atoms 01637 agp.getEAMask(eaIndex)->updateMask(); 01638 en = agp.getEAMask(eaIndex)->mask()->getStorage().first(); 01639 en_end = agp.getEAMask(eaIndex)->mask()->getStorage().end(); 01640 while (en < en_end) { 01641 if (reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary()) { 01642 // do not extend the variable domain (this is counterproductive) 01643 if ( domain->getFact(*en) ) { { 01644 // avoid that EA is true in I and false in I u -X 01645 Nogood ng = inputNogood; 01646 ng.insert(NogoodContainer::createLiteral(*en, true)); 01647 ng.insert(NogoodContainer::createLiteral(interpretationShadow[*en], false)); 01648 ufsDetectionProblem.addNogood(ng); 01649 } 01650 { 01651 // avoid that EA is false in I and true in I u -X 01652 Nogood ng = inputNogood; 01653 ng.insert(NogoodContainer::createLiteral(*en, false)); 01654 ng.insert(NogoodContainer::createLiteral(interpretationShadow[*en], true)); 01655 ufsDetectionProblem.addNogood(ng); 01656 } 01657 } 01658 } 01659 en++; 01660 } 01661 } 01662 } 01663 01664 01665 void AssumptionBasedUnfoundedSetChecker::constructUFSDetectionProblemAndInstantiateSolver() 01666 { 01667 01668 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidcudp, "Construct UFS Detection Problem"); 01669 NogoodSet ufsDetectionProblem; 01670 01671 #ifndef NDEBUG 01672 std::stringstream programstring; 01673 RawPrinter printer(programstring, reg); 01674 #endif 01675 01676 DBGLOG(DBG, "Constructing UFS detection problem"); 01677 01678 atomcnt = 0; 01679 01680 // create a hook atom 01681 OrdinaryAtom hatom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01682 hatom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01683 hookAtom = reg->storeOrdinaryGAtom(hatom); 01684 01685 // create problem encoding 01686 constructDomain(); 01687 constructUFSDetectionProblemFacts(ufsDetectionProblem); 01688 constructUFSDetectionProblemCreateAuxAtoms(); 01689 constructUFSDetectionProblemDefineAuxiliaries(ufsDetectionProblem); 01690 constructUFSDetectionProblemNonempty(ufsDetectionProblem); 01691 constructUFSDetectionProblemRestrictToSCC(ufsDetectionProblem); 01692 constructUFSDetectionProblemBasicEABehavior(ufsDetectionProblem); 01693 01694 DBGLOG(DBG, "N: Rules"); 01695 BOOST_FOREACH (ID ruleID, groundProgram.idb) { 01696 constructUFSDetectionProblemRule(ufsDetectionProblem, ruleID); 01697 } 01698 01699 // We need to freeze all variables which might be fixed by assumptions, 01700 // or whose truth values in the models are relevant. 01701 // This includes all domains atoms and their shadows. 01702 InterpretationPtr frozenInt; 01703 #if 0 01704 frozenInt.reset(new Interpretation(reg)); 01705 bm::bvector<>::enumerator en = domain->getStorage().first(); 01706 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01707 while (en < en_end) { 01708 frozenInt->setFact(*en); 01709 frozenInt->setFact(interpretationShadow[*en]); 01710 en++; 01711 } 01712 // However, since leaned clauses can only constraint frozen variables, 01713 // it seems to be better to freeze all. 01714 #endif 01715 01716 // finally, instantiate the solver for the constructed search problem 01717 DBGLOG(DBG, "Unfounded Set Detection Problem: " << ufsDetectionProblem); 01718 solver = SATSolver::getInstance(ctx, ufsDetectionProblem, frozenInt); 01719 01720 // remember the number of rules respected to far to allow for incremental extension 01721 problemRuleCount = groundProgram.idb.size(); 01722 } 01723 01724 01725 void AssumptionBasedUnfoundedSetChecker::expandUFSDetectionProblemAndReinstantiateSolver() 01726 { 01727 01728 DBGLOG(DBG, "Extend UFS detection problem"); 01729 01730 // remember old domain 01731 InterpretationPtr oldDomain = domain; 01732 01733 // compute (strictly) new domain 01734 domain = InterpretationPtr(new Interpretation(reg)); 01735 constructDomain(); 01736 domain->getStorage() -= oldDomain->getStorage(); 01737 01738 // create a hook atom 01739 OrdinaryAtom hatom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_ATOM_HIDDEN); 01740 hatom.tuple.push_back(reg->getAuxiliaryConstantSymbol('x', ID(0, atomcnt++))); 01741 hookAtom = reg->storeOrdinaryGAtom(hatom); 01742 01743 NogoodSet ufsDetectionProblem; 01744 01745 // add domain-specific part for the new domain 01746 constructUFSDetectionProblemFacts(ufsDetectionProblem); 01747 constructUFSDetectionProblemCreateAuxAtoms(); 01748 constructUFSDetectionProblemDefineAuxiliaries(ufsDetectionProblem); 01749 01750 // add global part for the new overall program 01751 domain->add(*oldDomain); 01752 constructUFSDetectionProblemNonempty(ufsDetectionProblem); 01753 constructUFSDetectionProblemRestrictToSCC(ufsDetectionProblem); 01754 constructUFSDetectionProblemBasicEABehavior(ufsDetectionProblem); 01755 01756 DBGLOG(DBG, "N: New Rules"); 01757 while (problemRuleCount < groundProgram.idb.size()) { 01758 ID ruleID = groundProgram.idb[problemRuleCount]; 01759 constructUFSDetectionProblemRule(ufsDetectionProblem, ruleID); 01760 problemRuleCount++; 01761 } 01762 01763 // finally, reinstantiate the solver for the constructed search problem 01764 InterpretationPtr frozenInt; 01765 DBGLOG(DBG, "Unfounded Set Detection Problem Addition: " << ufsDetectionProblem); 01766 solver->addNogoodSet(ufsDetectionProblem); 01767 } 01768 01769 01770 void AssumptionBasedUnfoundedSetChecker::setAssumptions(InterpretationConstPtr compatibleSet, const std::set<ID>& skipProgram) 01771 { 01772 01773 std::vector<ID> assumptions; 01774 01775 bm::bvector<>::enumerator en = domain->getStorage().first(); 01776 bm::bvector<>::enumerator en_end = domain->getStorage().end(); 01777 DBGLOG(DBG, "A: Encoding interpretation"); 01778 while (en < en_end) { 01779 DBGLOG(DBG, interpretationShadow[*en] << "=" << compatibleSet->getFact(*en)); 01780 assumptions.push_back(ID(compatibleSet->getFact(*en) ? 0 : ID::NAF_MASK, interpretationShadow[*en])); 01781 en++; 01782 } 01783 01784 en = domain->getStorage().first(); 01785 en_end = domain->getStorage().end(); 01786 DBGLOG(DBG, "A: Intersection of U with I"); 01787 while (en < en_end) { 01788 // do not set an ordinary atom which is false in I 01789 if (!reg->ogatoms.getIDByAddress(*en).isExternalAuxiliary() || mode == Ordinary) { 01790 if (!compatibleSet->getFact(*en)) { 01791 DBGLOG(DBG, *en << "=0"); 01792 assumptions.push_back(ID::nafLiteralFromAtom(reg->ogatoms.getIDByAddress(*en))); 01793 } 01794 } 01795 en++; 01796 } 01797 01798 // the ufs must not contain a head atom of an ignored rule 01799 // (otherwise we cannot guarantee that the ufs remains an ufs for completed interpretations) 01800 DBGLOG(DBG, "A: Ignored rules"); 01801 { 01802 BOOST_FOREACH (ID ruleId, skipProgram) { 01803 const Rule& rule = reg->rules.getByID(ruleId); 01804 BOOST_FOREACH (ID h, rule.head) { 01805 if (domain->getFact(h.address)) { 01806 DBGLOG(DBG, h.address << "=0"); 01807 assumptions.push_back(ID::nafLiteralFromAtom(reg->ogatoms.getIDByAddress(h.address))); 01808 } 01809 } 01810 } 01811 } 01812 01813 // let the CURRENT(!) hook atom be true 01814 assumptions.push_back(ID::posLiteralFromAtom(hookAtom)); 01815 01816 #ifndef NDEBUG 01817 BOOST_FOREACH (ID a, assumptions) { 01818 DBGLOG(DBG, "Assumption: " << a.address << "=" << !a.isNaf()); 01819 } 01820 #endif 01821 01822 solver->restartWithAssumptions(assumptions); 01823 } 01824 01825 01826 AssumptionBasedUnfoundedSetChecker::AssumptionBasedUnfoundedSetChecker( 01827 ProgramCtx& ctx, 01828 const OrdinaryASPProgram& groundProgram, 01829 InterpretationConstPtr componentAtoms, 01830 SimpleNogoodContainerPtr ngc) : 01831 UnfoundedSetChecker(ctx, groundProgram, componentAtoms, ngc) 01832 { 01833 01834 reg = ctx.registry(); 01835 learnedNogoodsFromMainSearch = 0; 01836 01837 #ifndef NDEBUG 01838 std::stringstream programstring; 01839 RawPrinter printer(programstring, reg); 01840 if (groundProgram.edb) programstring << "EDB: " << *groundProgram.edb << std::endl; 01841 programstring << "IDB:" << std::endl; 01842 BOOST_FOREACH (ID ruleId, groundProgram.idb) { 01843 printer.print(ruleId); 01844 programstring << std::endl; 01845 } 01846 DBGLOG(DBG, "Computing unfounded set of program:" << std::endl << programstring.str()); 01847 #endif 01848 01849 // construct the UFS detection problem 01850 constructUFSDetectionProblemAndInstantiateSolver(); 01851 } 01852 01853 01854 AssumptionBasedUnfoundedSetChecker::AssumptionBasedUnfoundedSetChecker( 01855 BaseModelGenerator& mg, 01856 ProgramCtx& ctx, 01857 const OrdinaryASPProgram& groundProgram, 01858 const AnnotatedGroundProgram& agp, 01859 InterpretationConstPtr componentAtoms, 01860 SimpleNogoodContainerPtr ngc) : 01861 UnfoundedSetChecker(&mg, ctx, groundProgram, agp, componentAtoms, ngc) 01862 { 01863 01864 reg = ctx.registry(); 01865 learnedNogoodsFromMainSearch = 0; 01866 01867 #ifndef NDEBUG 01868 std::stringstream programstring; 01869 RawPrinter printer(programstring, reg); 01870 if (groundProgram.edb) programstring << "EDB: " << *groundProgram.edb << std::endl; 01871 programstring << "IDB:" << std::endl; 01872 BOOST_FOREACH (ID ruleId, groundProgram.idb) { 01873 printer.print(ruleId); 01874 programstring << std::endl; 01875 } 01876 DBGLOG(DBG, "Computing unfounded set of program:" << std::endl << programstring.str()); 01877 #endif 01878 01879 // construct the UFS detection problem 01880 constructUFSDetectionProblemAndInstantiateSolver(); 01881 } 01882 01883 01884 std::pair<bool, Nogood> AssumptionBasedUnfoundedSetChecker::nogoodTransformation(Nogood ng, InterpretationConstPtr assignment) 01885 { 01886 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidtransform, "UFS nogood transformation"); 01887 01888 // Note: this transformation must not depend on the compatible set! 01889 std::pair<bool, Nogood> ret; 01890 bool& valid = ret.first; valid = true; 01891 Nogood& ngAdd = ret.second; 01892 01893 BOOST_FOREACH (ID id, ng) { 01894 // we have to requery the ID because nogoods strip off unnecessary information (e.g. property flags) 01895 if (reg->ogatoms.getIDByAddress(id.address).isExternalAuxiliary()) { 01896 01897 ID useID = id; 01898 01899 // transform negative replacements to positive ones 01900 OrdinaryAtom ogatom = reg->ogatoms.getByID(id); 01901 if (ogatom.tuple[0] == reg->getAuxiliaryConstantSymbol('n', reg->getIDByAuxiliaryConstantSymbol(ogatom.tuple[0]))) { 01902 ogatom.tuple[0] = reg->getAuxiliaryConstantSymbol('r', reg->getIDByAuxiliaryConstantSymbol(ogatom.tuple[0])); 01903 useID = reg->storeOrdinaryGAtom(ogatom); 01904 // flip truth value 01905 useID.kind |= ID::NAF_MASK; 01906 } 01907 01908 // do not add a nogood if it extends the variable domain (this is counterproductive) 01909 if ( !domain->getFact(useID.address) ) { 01910 DBGLOG(DBG, "Skipping because " << useID.address << " expands the domain"); 01911 valid = false; 01912 break; 01913 } 01914 else { 01915 DBGLOG(DBG, "Inserting EA-Aux " << (useID.isNaf() ? "-" : "") << useID.address); 01916 ngAdd.insert(NogoodContainer::createLiteral(useID)); 01917 } 01918 } 01919 else { 01920 // input atom 01921 01922 // we have the following relations between sign S of the atom in the nogood, truth in assignment C and the unfounded set 01923 // S=positive, C=false --> nogood can never fire, skip it 01924 // S=positive, C=true --> nogood fires if the atom is NOT in the unfounded set (because it is not in the domain or it is false) 01925 // S=negative, C=true --> nogood fires if the atom IS in the unfounded set (because then it is false in I u -X) 01926 // S=negative, C=false --> nogood will always fire (wrt. this literal), skip the literal 01927 if (!id.isNaf()) { 01928 // positive 01929 ngAdd.insert(NogoodContainer::createLiteral(interpretationShadow[id.address], true)); 01930 // true in I --> nogood fires if X does not contain the atom 01931 if ( domain->getFact(id.address) ) { 01932 DBGLOG(DBG, "Inserting ordinary -" << id.address << " because it is true in I"); 01933 ngAdd.insert(NogoodContainer::createLiteral(id.address, false)); 01934 } 01935 else { 01936 DBGLOG(DBG, "Skipping ordinary " << id.address << " because it is not in the domain and can therefore never be in the unfounded set"); 01937 } 01938 } 01939 else { 01940 // negative 01941 DBGLOG(DBG, "Inserting " << id.address << " because it is false in I u -X if it is in X"); 01942 ngAdd.insert(NogoodContainer::createLiteral(residualShadow[id.address], false)); 01943 } 01944 } 01945 } 01946 DBGLOG(DBG, "Adding transformed nogood " << ngAdd << " (valid: " << valid << ")"); 01947 return ret; 01948 } 01949 01950 01951 void AssumptionBasedUnfoundedSetChecker::learnNogoodsFromMainSearch(bool reset) 01952 { 01953 01954 // add newly learned nogoods from the main search (in transformed form) 01955 if (!!ngc) { 01956 // detect resets of the nogood container 01957 if (learnedNogoodsFromMainSearch > ngc->getNogoodCount() || reset) learnedNogoodsFromMainSearch = 0; 01958 DBGLOG(DBG, "O: Adding valid input-output relationships from nogood container"); 01959 for (int i = learnedNogoodsFromMainSearch; i < ngc->getNogoodCount(); ++i) { 01960 const Nogood& ng = ngc->getNogood(i); 01961 if (ng.isGround()) { 01962 DBGLOG(DBG, "Processing learned nogood " << ng.getStringRepresentation(reg)); 01963 01964 // this transformation must not depend on the compatible set! 01965 std::pair<bool, Nogood> transformed = nogoodTransformation(ng, InterpretationConstPtr()); 01966 if (transformed.first) { 01967 solver->addNogood(transformed.second); 01968 } 01969 01970 if (ctx.config.getOption("ExternalAtomVerificationFromLearnedNogoods")) { 01971 eavTree.addNogood(ng, reg, true); 01972 } 01973 } 01974 } 01975 learnedNogoodsFromMainSearch = ngc->getNogoodCount(); 01976 } 01977 } 01978 01979 01980 std::vector<IDAddress> AssumptionBasedUnfoundedSetChecker::getUnfoundedSet(InterpretationConstPtr compatibleSet, const std::set<ID>& skipProgram) 01981 { 01982 01983 DBGLOG(DBG, "Performing UFS Check wrt. " << *compatibleSet); 01984 01985 // check if the instance needs to be extended 01986 DBGLOG(DBG, "Checking if problem encoding needs to be expanded"); 01987 if (groundProgram.idb.size() > problemRuleCount) { 01988 DBGLOG(DBG, "Problem encoding needs to be expanded"); 01989 expandUFSDetectionProblemAndReinstantiateSolver(); 01990 } 01991 else { 01992 DBGLOG(DBG, "Problem encoding does not need to be expanded"); 01993 } 01994 01995 // learn from main search 01996 learnNogoodsFromMainSearch(true); 01997 01998 // load assumptions 01999 setAssumptions(compatibleSet, skipProgram); 02000 02001 // we need the compatible set also without external atom replacement atoms 02002 InterpretationConstPtr compatibleSetWithoutAux = compatibleSet->getInterpretationWithoutExternalAtomAuxiliaries(); 02003 02004 int mCnt = 0; 02005 02006 #ifdef DLVHEX_BENCHMARK 02007 DLVHEX_BENCHMARK_REGISTER(ufscheck, "UFS Check"); 02008 DLVHEX_BENCHMARK_REGISTER(oufscheck, "Ordinary UFS Check"); 02009 if( mode == WithExt ) { 02010 DLVHEX_BENCHMARK_START(ufscheck); 02011 } 02012 else { 02013 DLVHEX_BENCHMARK_START(oufscheck); 02014 } 02015 BOOST_SCOPE_EXIT( (ufscheck)(oufscheck)(mode) ) { 02016 if( mode == WithExt ) { 02017 DLVHEX_BENCHMARK_STOP(ufscheck); 02018 } 02019 else { 02020 DLVHEX_BENCHMARK_STOP(oufscheck); 02021 } 02022 } BOOST_SCOPE_EXIT_END 02023 #endif 02024 02025 DLVHEX_BENCHMARK_REGISTER(sidufsenum, "UFS-Detection Problem Solving"); 02026 if (mode == WithExt) { 02027 DLVHEX_BENCHMARK_START(sidufsenum); 02028 } 02029 InterpretationConstPtr model = solver->getNextModel(); 02030 if (mode == WithExt) { 02031 DLVHEX_BENCHMARK_STOP(sidufsenum); 02032 } 02033 while ( model != InterpretationConstPtr()) { 02034 if (mode == WithExt) { 02035 DLVHEX_BENCHMARK_REGISTER_AND_COUNT(ufscandidates, "Checked UFS candidates", 1); 02036 } 02037 02038 // check if the model is actually an unfounded set 02039 DBGLOG(DBG, "Got UFS candidate: " << *model); 02040 mCnt++; 02041 02042 if (mode == Ordinary || isUnfoundedSet(compatibleSet, compatibleSetWithoutAux, model)) { 02043 DBGLOG(DBG, "Found UFS: " << *model << " (interpretation: " << *compatibleSet << ")"); 02044 02045 std::vector<IDAddress> ufs; 02046 02047 bm::bvector<>::enumerator en = model->getStorage().first(); 02048 bm::bvector<>::enumerator en_end = model->getStorage().end(); 02049 while (en < en_end) { 02050 if (domain->getFact(*en)) ufs.push_back(*en); 02051 en++; 02052 } 02053 02054 DBGLOG(DBG, "Enumerated " << mCnt << " UFS candidates"); 02055 02056 if (mode == WithExt) { 02057 DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sidfailedufscheckcount, "Failed UFS Checks", 1); 02058 } 02059 02060 return ufs; 02061 } 02062 else { 02063 DBGLOG(DBG, "No UFS: " << *model); 02064 } 02065 02066 if (mode == WithExt) { 02067 DLVHEX_BENCHMARK_START(sidufsenum); 02068 } 02069 model = solver->getNextModel(); 02070 if (mode == WithExt) { 02071 DLVHEX_BENCHMARK_STOP(sidufsenum); 02072 } 02073 } 02074 02075 DBGLOG(DBG, "Enumerated " << mCnt << " UFS candidates"); 02076 02077 // no ufs 02078 std::vector<IDAddress> ufs; 02079 return ufs; 02080 } 02081 02082 02083 /* 02084 * Manager for unfounded set checkers 02085 * 02086 * The manager takes care of creating UFS checkers for all components of the program which require a UFS check. 02087 * During search, the manager will call all unfounded set checkers until a definite answer to the query can be given. 02088 */ 02089 02090 UnfoundedSetCheckerManager::UnfoundedSetCheckerManager( 02091 BaseModelGenerator& mg, 02092 ProgramCtx& ctx, 02093 const AnnotatedGroundProgram& agp, 02094 bool choiceRuleCompatible, 02095 SimpleNogoodContainerPtr ngc) : 02096 mg(&mg), ctx(ctx), agp(agp), lastAGPComponentCount(0), choiceRuleCompatible(choiceRuleCompatible), ngc(ngc) 02097 { 02098 02099 computeChoiceRuleCompatibility(choiceRuleCompatible); 02100 if (!ctx.config.getOption("LazyUFSCheckerInitialization")) initializeUnfoundedSetCheckers(); 02101 02102 /* 02103 // Test incremental definition of an instance 02104 std::cout << "=================" << std::endl; 02105 OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 02106 at1.tuple.push_back(ctx.registry()->storeConstantTerm("a")); 02107 ID at1ID = ctx.registry()->storeOrdinaryGAtom(at1); 02108 02109 OrdinaryAtom at2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 02110 at2.tuple.push_back(ctx.registry()->storeConstantTerm("b")); 02111 ID at2ID = ctx.registry()->storeOrdinaryGAtom(at2); 02112 02113 OrdinaryAtom at3(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 02114 at3.tuple.push_back(ctx.registry()->storeConstantTerm("c")); 02115 ID at3ID = ctx.registry()->storeOrdinaryGAtom(at3); 02116 02117 InterpretationPtr frozen(new Interpretation(ctx.registry())); 02118 //frozen->setFact(at1ID.address); 02119 //frozen->setFact(at2ID.address); 02120 frozen->setFact(at3ID.address); 02121 02122 Rule r1(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR); 02123 r1.head.push_back(at1ID); 02124 r1.body.push_back(ID::posLiteralFromAtom(at2ID)); 02125 std::vector<ID> idb1; 02126 idb1.push_back(ctx.registry()->storeRule(r1)); 02127 //OrdinaryASPProgram p1(ctx.registry(), idb1, InterpretationPtr(new Interpretation(ctx.registry())), ctx.maxint, InterpretationPtr()); 02128 02129 Rule r2(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR); 02130 r2.head.push_back(at2ID); 02131 r2.body.push_back(ID::posLiteralFromAtom(at1ID)); 02132 idb1.push_back(ctx.registry()->storeRule(r2)); 02133 02134 Rule r3(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR); 02135 r3.head.push_back(at2ID); 02136 r3.body.push_back(ID::posLiteralFromAtom(at3ID)); 02137 idb1.push_back(ctx.registry()->storeRule(r3)); 02138 02139 OrdinaryASPProgram p1(ctx.registry(), idb1, InterpretationPtr(new Interpretation(ctx.registry())), ctx.maxint, InterpretationPtr()); 02140 02141 GenuineGroundSolverPtr ss = GenuineGroundSolver::getInstance(ctx, p1, frozen); 02142 02143 std::vector<ID> ass; 02144 ass.push_back(ID::nafLiteralFromAtom(at3ID)); 02145 ss->restartWithAssumptions(ass); 02146 std::cout << "It 1" << std::endl; 02147 InterpretationConstPtr model; 02148 while (!!(model = ss->getNextModel())){ 02149 std::cout << *model << std::endl; 02150 } 02151 02152 std::cout << "=================" << std::endl; 02153 */ 02154 } 02155 02156 02157 UnfoundedSetCheckerManager::UnfoundedSetCheckerManager( 02158 ProgramCtx& ctx, 02159 const AnnotatedGroundProgram& agp, 02160 bool choiceRuleCompatible) : 02161 ctx(ctx), mg(0), agp(agp), choiceRuleCompatible(choiceRuleCompatible), lastAGPComponentCount(0) 02162 { 02163 02164 computeChoiceRuleCompatibility(choiceRuleCompatible); 02165 if (!ctx.config.getOption("LazyUFSCheckerInitialization")) initializeUnfoundedSetCheckers(); 02166 } 02167 02168 02169 void UnfoundedSetCheckerManager::initializeUnfoundedSetCheckers() 02170 { 02171 02172 bool flpdc_head = (ctx.config.getOption("FLPDecisionCriterionHead") != 0); 02173 bool flpdc_e = (ctx.config.getOption("FLPDecisionCriterionE") != 0); 02174 02175 if (ctx.config.getOption("UFSCheckMonolithic")) { 02176 if (mg && (agp.hasECycles() || !flpdc_e)) { 02177 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02178 (0, instantiateUnfoundedSetChecker(*mg, ctx, agp.getGroundProgram(), agp, InterpretationConstPtr(), ngc)) 02179 ); 02180 } 02181 else { 02182 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02183 (0, instantiateUnfoundedSetChecker(ctx, agp.getGroundProgram(), InterpretationConstPtr(), ngc)) 02184 ); 02185 } 02186 } 02187 else { 02188 for (int comp = 0; comp < agp.getComponentCount(); ++comp) { 02189 if ((!agp.hasHeadCycles(comp) && flpdc_head) && !intersectsWithNonHCFDisjunctiveRules[comp] && (!mg || !agp.hasECycles(comp) && flpdc_e)) { 02190 DBGLOG(DBG, "Skipping component " << comp << " because it contains neither head-cycles nor e-cycles"); 02191 continue; 02192 } 02193 02194 if (mg && (agp.hasECycles(comp) || !flpdc_e)) { 02195 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02196 (comp, instantiateUnfoundedSetChecker(*mg, ctx, agp.getProgramOfComponent(comp), agp, agp.getAtomsOfComponent(comp), ngc)) 02197 ); 02198 } 02199 else { 02200 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02201 (comp, instantiateUnfoundedSetChecker(ctx, agp.getProgramOfComponent(comp), agp.getAtomsOfComponent(comp), ngc)) 02202 ); 02203 } 02204 } 02205 } 02206 } 02207 02208 02209 void UnfoundedSetCheckerManager::computeChoiceRuleCompatibility(bool choiceRuleCompatible) 02210 { 02211 02212 for (int comp = 0; comp < agp.getComponentCount(); ++comp) { 02213 computeChoiceRuleCompatibilityForComponent(choiceRuleCompatible, comp); 02214 } 02215 } 02216 02217 02218 void UnfoundedSetCheckerManager::computeChoiceRuleCompatibilityForComponent(bool choiceRuleCompatible, int comp) 02219 { 02220 02221 if (agp.hasHeadCycles(comp) || !choiceRuleCompatible) { 02222 intersectsWithNonHCFDisjunctiveRules.push_back(false); 02223 } 02224 else { 02225 // Check if the component contains a disjunctive non-HCF rule 02226 // Note that this does not necessarily mean that the component is non-NCF! 02227 // Example: 02228 // a v b v c. 02229 // a :- b. 02230 // b :- a. 02231 // a :- c. 02232 // d :- c. 02233 // c :- d. 02234 // The program has the following components: 02235 // {a, b} with rules a v b v c. 02236 // a :- b. 02237 // b :- a. 02238 // a :- c. 02239 // {c, d} with rules a v b v c. 02240 // d :- c. 02241 // c :- d. 02242 // Note that the component {c, d} contains a Non-HCF disjunctive rule (a v b v c.), but is not Non-HCF itself. 02243 // Therefore, the optimization would skip the UFS check for the component {c, d} (and do it for {a, b}). 02244 // 02245 // If disjunctive rules are considered as such, this is sufficient because the (polynomial) UFS check implemented 02246 // directly in the reasoner detects the unfounded set: 02247 // A candidate is {c, a, b, d}, but none of these atoms can use the rule a v b v c. as source because it is satisfied independently of {a}, {b} and {c}. 02248 // However, if disjunctive rules are transformed to choice rules, then a v b v c. becomes 1{a, b, c} and MULTIPLE atoms may use it as source. 02249 // 02250 // Therefore, the (exponential) UFS check in this class is not only necessary for Non-HCF-components, but also for HCF-components which contain disjunctive rules 02251 // which also also in some other Non-HCF-components. 02252 bool dh = false; 02253 BOOST_FOREACH (ID ruleID, agp.getProgramOfComponent(comp).idb) { 02254 if (agp.containsHeadCycles(ruleID)) { 02255 dh = true; 02256 break; 02257 } 02258 } 02259 intersectsWithNonHCFDisjunctiveRules.push_back(dh); 02260 } 02261 } 02262 02263 02264 UnfoundedSetCheckerPtr UnfoundedSetCheckerManager::instantiateUnfoundedSetChecker( 02265 ProgramCtx& ctx, 02266 const OrdinaryASPProgram& groundProgram, 02267 InterpretationConstPtr componentAtoms, 02268 SimpleNogoodContainerPtr ngc) 02269 { 02270 02271 if (ctx.config.getOption("UFSCheckAssumptionBased") && false) { 02272 DBGLOG(DBG, "instantiateUnfoundedSetChecker ordinary/assumption-based"); 02273 return UnfoundedSetCheckerPtr(new AssumptionBasedUnfoundedSetChecker(ctx, groundProgram, componentAtoms, ngc)); 02274 } 02275 else { 02276 DBGLOG(DBG, "instantiateUnfoundedSetChecker ordinary/encoding-based"); 02277 return UnfoundedSetCheckerPtr(new EncodingBasedUnfoundedSetChecker(ctx, groundProgram, componentAtoms, ngc)); 02278 } 02279 } 02280 02281 02282 UnfoundedSetCheckerPtr UnfoundedSetCheckerManager::instantiateUnfoundedSetChecker( 02283 BaseModelGenerator& mg, 02284 ProgramCtx& ctx, 02285 const OrdinaryASPProgram& groundProgram, 02286 const AnnotatedGroundProgram& agp, 02287 InterpretationConstPtr componentAtoms, 02288 SimpleNogoodContainerPtr ngc) 02289 { 02290 02291 if (ctx.config.getOption("UFSCheckAssumptionBased")) { 02292 DBGLOG(DBG, "instantiateUnfoundedSetChecker external/assumption-based"); 02293 return UnfoundedSetCheckerPtr(new AssumptionBasedUnfoundedSetChecker(mg, ctx, groundProgram, agp, componentAtoms, ngc)); 02294 } 02295 else { 02296 DBGLOG(DBG, "instantiateUnfoundedSetChecker external/encoding-based"); 02297 return UnfoundedSetCheckerPtr(new EncodingBasedUnfoundedSetChecker(mg, ctx, groundProgram, agp, componentAtoms, ngc)); 02298 } 02299 } 02300 02301 02302 void UnfoundedSetCheckerManager::learnNogoodsFromMainSearch(bool reset) 02303 { 02304 02305 // notify all unfounded set checkers 02306 typedef std::pair<int, UnfoundedSetCheckerPtr> Pair; 02307 BOOST_FOREACH (Pair p, preparedUnfoundedSetCheckers) { 02308 p.second->learnNogoodsFromMainSearch(reset); 02309 } 02310 } 02311 02312 02313 std::vector<IDAddress> UnfoundedSetCheckerManager::getUnfoundedSet( 02314 InterpretationConstPtr interpretation, 02315 const std::set<ID>& skipProgram, 02316 SimpleNogoodContainerPtr ngc) 02317 { 02318 02319 bool flpdc_head = (ctx.config.getOption("FLPDecisionCriterionHead") != 0); 02320 bool flpdc_e = (ctx.config.getOption("FLPDecisionCriterionE") != 0); 02321 bool flpdc_emi = (ctx.config.getOption("FLPDecisionCriterionEMI") != 0); 02322 02323 // in incremental mode we need to proceed as we can decide the FLP criterion only after checking each component for necessary extensions 02324 if (!ctx.config.getOption("IncrementalGrounding")) { 02325 if ( (!agp.hasHeadCycles() && flpdc_head) && (!mg || flpdc_e && (!agp.hasECycles() || (flpdc_emi && !agp.hasECycles(interpretation)))) ) { 02326 DBGLOG(DBG, "Skipping UFS check program it contains neither head-cycles nor e-cycles"); 02327 return std::vector<IDAddress>(); 02328 } 02329 } 02330 02331 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "UnfoundedSetChkMgr::getUFS"); 02332 std::vector<IDAddress> ufs; 02333 if (ctx.config.getOption("UFSCheckMonolithic")) { 02334 if (preparedUnfoundedSetCheckers.size() > 0 && agp.getComponentCount() > lastAGPComponentCount) { 02335 } 02336 lastAGPComponentCount = agp.getComponentCount(); 02337 02338 DBGLOG(DBG, "UnfoundedSetCheckerManager::getUnfoundedSet monolithic"); 02339 if (mg && (!flpdc_e || (agp.hasECycles() && (!flpdc_emi || agp.hasECycles(interpretation))))) { 02340 DBGLOG(DBG, "Checking UFS under consideration of external atoms"); 02341 if (preparedUnfoundedSetCheckers.size() == 0) { 02342 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02343 (0, instantiateUnfoundedSetChecker(*mg, ctx, agp.getGroundProgram(), agp, InterpretationConstPtr(), ngc)) 02344 ); 02345 } 02346 UnfoundedSetCheckerPtr ufsc = preparedUnfoundedSetCheckers.find(0)->second; 02347 ufs = ufsc->getUnfoundedSet(interpretation, skipProgram); 02348 if (ufs.size() > 0) { 02349 DBGLOG(DBG, "Found a UFS"); 02350 ufsnogood = ufsc->getUFSNogood(ufs, interpretation); 02351 } 02352 } 02353 else { 02354 DBGLOG(DBG, "Checking UFS without considering external atoms"); 02355 if (preparedUnfoundedSetCheckers.size() == 0) { 02356 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02357 (0, instantiateUnfoundedSetChecker(ctx, agp.getGroundProgram(), InterpretationConstPtr(), ngc)) 02358 ); 02359 } 02360 UnfoundedSetCheckerPtr ufsc = preparedUnfoundedSetCheckers.find(0)->second; 02361 ufs = ufsc->getUnfoundedSet(interpretation, skipProgram); 02362 if (ufs.size() > 0) { 02363 DBGLOG(DBG, "Found a UFS"); 02364 ufsnogood = ufsc->getUFSNogood(ufs, interpretation); 02365 } 02366 } 02367 } 02368 else { 02369 for (int i = intersectsWithNonHCFDisjunctiveRules.size(); i < agp.getComponentCount(); ++i) { 02370 computeChoiceRuleCompatibilityForComponent(choiceRuleCompatible, i); 02371 } 02372 02373 // search in each component for unfounded sets 02374 DBGLOG(DBG, "UnfoundedSetCheckerManager::getUnfoundedSet component-wise"); 02375 for (int comp = 0; comp < agp.getComponentCount(); ++comp) { 02376 if ( (!agp.hasHeadCycles(comp) && flpdc_head) && !intersectsWithNonHCFDisjunctiveRules[comp] && (!mg || flpdc_e && (!agp.hasECycles(comp) || (flpdc_emi && !agp.hasECycles(comp, interpretation)))) ) { 02377 DBGLOG(DBG, "Skipping component " << comp << " because it contains neither head-cycles nor e-cycles"); 02378 continue; 02379 } 02380 02381 DBGLOG(DBG, "Checking for UFS in component " << comp); 02382 if ( mg && (!flpdc_e || (agp.hasECycles(comp) && (!flpdc_emi || agp.hasECycles(comp, interpretation)))) ) { 02383 DBGLOG(DBG, "Checking UFS under consideration of external atoms"); 02384 if (preparedUnfoundedSetCheckers.find(comp) == preparedUnfoundedSetCheckers.end()) { 02385 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02386 (comp, instantiateUnfoundedSetChecker(*mg, ctx, agp.getProgramOfComponent(comp), agp, agp.getAtomsOfComponent(comp), ngc)) 02387 ); 02388 } 02389 UnfoundedSetCheckerPtr ufsc = preparedUnfoundedSetCheckers.find(comp)->second; 02390 ufs = ufsc->getUnfoundedSet(interpretation, skipProgram); 02391 if (ufs.size() > 0) { 02392 DBGLOG(DBG, "Found a UFS"); 02393 ufsnogood = ufsc->getUFSNogood(ufs, interpretation); 02394 break; 02395 } 02396 } 02397 else { 02398 DBGLOG(DBG, "Checking UFS without considering external atoms"); 02399 if (preparedUnfoundedSetCheckers.find(comp) == preparedUnfoundedSetCheckers.end()) { 02400 preparedUnfoundedSetCheckers.insert(std::pair<int, UnfoundedSetCheckerPtr> 02401 (comp, instantiateUnfoundedSetChecker(ctx, agp.getProgramOfComponent(comp), agp.getAtomsOfComponent(comp), ngc)) 02402 ); 02403 } 02404 UnfoundedSetCheckerPtr ufsc = preparedUnfoundedSetCheckers.find(comp)->second; 02405 ufs = ufsc->getUnfoundedSet(interpretation, skipProgram); 02406 if (ufs.size() > 0) { 02407 DBGLOG(DBG, "Found a UFS"); 02408 ufsnogood = ufsc->getUFSNogood(ufs, interpretation); 02409 break; 02410 } 02411 } 02412 } 02413 } 02414 02415 // no ufs found 02416 if (ufs.size() == 0) { 02417 DBGLOG(DBG, "No component contains a UFS"); 02418 } 02419 else { 02420 #ifndef NDEBUG 02421 // // all elements in the unfounded set must be in the domain 02422 // BOOST_FOREACH (IDAddress adr, ufs){ 02423 // assert(domain->getFact(adr) && "UFS contains a non-domain atom"); 02424 // } 02425 #endif 02426 } 02427 return ufs; 02428 } 02429 02430 02431 std::vector<IDAddress> UnfoundedSetCheckerManager::getUnfoundedSet(InterpretationConstPtr interpretation) 02432 { 02433 static std::set<ID> emptySkipProgram; 02434 return getUnfoundedSet(interpretation, emptySkipProgram); 02435 } 02436 02437 02438 Nogood UnfoundedSetCheckerManager::getLastUFSNogood() const 02439 { 02440 #ifndef NDEBUG 02441 // // all elements in the nogood must be in the domain 02442 // BOOST_FOREACH (ID lit, ufsnogood){ 02443 // assert(domain->getFact(lit.address) && "UFS nogood contains a non-domain atom"); 02444 // } 02445 #endif 02446 return ufsnogood; 02447 } 02448 02449 02450 DLVHEX_NAMESPACE_END 02451 02452 // vim:expandtab:ts=4:sw=4: 02453 // mode: C++ 02454 // End: