dlvhex  2.5.0
src/UnfoundedSetChecker.cpp
Go to the documentation of this file.
00001 /* dlvhex -- Answer-Set Programming with external interfaces.
00002  * Copyright (C) 2005-2007 Roman Schindlauer
00003  * Copyright (C) 2006-2015 Thomas Krennwallner
00004  * Copyright (C) 2009-2016 Peter Schüller
00005  * Copyright (C) 2011-2016 Christoph Redl
00006  * Copyright (C) 2015-2016 Tobias Kaminski
00007  * Copyright (C) 2015-2016 Antonius Weinzierl
00008  *
00009  * This file is part of dlvhex.
00010  *
00011  * dlvhex is free software; you can redistribute it and/or modify it
00012  * under the terms of the GNU Lesser General Public License as
00013  * published by the Free Software Foundation; either version 2.1 of
00014  * the License, or (at your option) any later version.
00015  *
00016  * dlvhex is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with dlvhex; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00024  * 02110-1301 USA.
00025  */
00026 
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: