dlvhex  2.5.0
src/GenuineGuessAndCheckModelGenerator.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
00037 
00038 #include <fstream>
00039 
00040 #include "dlvhex2/GenuineGuessAndCheckModelGenerator.h"
00041 #include "dlvhex2/Logger.h"
00042 #include "dlvhex2/Registry.h"
00043 #include "dlvhex2/Printer.h"
00044 #include "dlvhex2/ASPSolver.h"
00045 #include "dlvhex2/ASPSolverManager.h"
00046 #include "dlvhex2/ProgramCtx.h"
00047 #include "dlvhex2/PluginInterface.h"
00048 #include "dlvhex2/Benchmarking.h"
00049 #include "dlvhex2/InternalGroundDASPSolver.h"
00050 #include "dlvhex2/UnfoundedSetChecker.h"
00051 
00052 #include <bm/bmalgo.h>
00053 
00054 #include <boost/foreach.hpp>
00055 #include <boost/unordered_map.hpp>
00056 #include <boost/property_map/property_map.hpp>
00057 #include <boost/graph/breadth_first_search.hpp>
00058 #include <boost/graph/visitors.hpp>
00059 #include <boost/graph/depth_first_search.hpp>
00060 #include <boost/graph/properties.hpp>
00061 #include <boost/scoped_ptr.hpp>
00062 
00063 DLVHEX_NAMESPACE_BEGIN
00064 
00065 GenuineGuessAndCheckModelGeneratorFactory::GenuineGuessAndCheckModelGeneratorFactory(
00066 ProgramCtx& ctx,
00067 const ComponentInfo& ci,
00068 ASPSolverManager::SoftwareConfigurationPtr externalEvalConfig):
00069 FLPModelGeneratorFactoryBase(ctx),
00070 externalEvalConfig(externalEvalConfig),
00071 ctx(ctx),
00072 ci(ci),
00073 outerEatoms(ci.outerEatoms)
00074 {
00075     // this model generator can handle any components
00076     // (and there is quite some room for more optimization)
00077 
00078     // just copy all rules and constraints to idb
00079     idb.reserve(ci.innerRules.size() + ci.innerConstraints.size());
00080     idb.insert(idb.end(), ci.innerRules.begin(), ci.innerRules.end());
00081     idb.insert(idb.end(), ci.innerConstraints.begin(), ci.innerConstraints.end());
00082 
00083     // create program for domain exploration
00084     if (ctx.config.getOption("LiberalSafety")) {
00085         // add domain predicates for all external atoms which are necessary to establish liberal domain-expansion safety
00086         // and extract the domain-exploration program from the IDB
00087         addDomainPredicatesAndCreateDomainExplorationProgram(ci, ctx, idb, deidb, deidbInnerEatoms, outerEatoms);
00088     }
00089 
00090     innerEatoms = ci.innerEatoms;
00091     // create guessing rules "gidb" for innerEatoms in all inner rules and constraints
00092     createEatomGuessingRules(ctx);
00093 
00094     // transform original innerRules and innerConstraints to xidb with only auxiliaries
00095     xidb.reserve(idb.size());
00096     std::back_insert_iterator<std::vector<ID> > inserter(xidb);
00097     std::transform(idb.begin(), idb.end(),
00098         inserter, boost::bind(&GenuineGuessAndCheckModelGeneratorFactory::convertRule, this, ctx, _1));
00099 
00100     // transform xidb for flp calculation
00101     if (ctx.config.getOption("FLPCheck")) createFLPRules();
00102 
00103     // output rules
00104     {
00105         std::ostringstream s;
00106         print(s, true);
00107         LOG(DBG,"GenuineGuessAndCheckModelGeneratorFactory(): " << s.str());
00108     }
00109 }
00110 
00111 void GenuineGuessAndCheckModelGeneratorFactory::addInconsistencyCauseFromSuccessor(const Nogood* cause){
00112     DBGLOG(DBG, "Inconsistency cause was added to model generator factory: " << cause->getStringRepresentation(ctx.registry()) << ", ogatoms in registry: " << ctx.registry()->ogatoms.getSize());
00113 
00114     // Store the nogood for future reinstantiations of the model generator.
00115     // To this end, store also the current maximum index of ground atoms in the registry;
00116     //   all atoms which are added later must be added with negative sign to the nogood because it was generated as inconsistency cause under the assumption that these atoms do not exist.
00117     succNogoods.push_back(std::pair<Nogood, int>(*cause, ctx.registry()->ogatoms.getSize()));
00118 }
00119 
00120 GenuineGuessAndCheckModelGeneratorFactory::ModelGeneratorPtr
00121 GenuineGuessAndCheckModelGeneratorFactory::createModelGenerator(
00122 InterpretationConstPtr input)
00123 {
00124     return ModelGeneratorPtr(new GenuineGuessAndCheckModelGenerator(*this, input));
00125 }
00126 
00127 
00128 std::ostream& GenuineGuessAndCheckModelGeneratorFactory::print(
00129 std::ostream& o) const
00130 {
00131     return print(o, true);
00132 }
00133 
00134 
00135 std::ostream& GenuineGuessAndCheckModelGeneratorFactory::print(
00136 std::ostream& o, bool verbose) const
00137 {
00138     // item separator
00139     std::string isep(" ");
00140     // group separator
00141     std::string gsep(" ");
00142     if( verbose ) {
00143         isep = "\n";
00144         gsep = "\n";
00145     }
00146     RawPrinter printer(o, ctx.registry());
00147     if( !outerEatoms.empty() ) {
00148         o << "outer Eatoms={" << gsep;
00149         printer.printmany(outerEatoms,isep);
00150         o << gsep << "}" << gsep;
00151     }
00152     if( !innerEatoms.empty() ) {
00153         o << "inner Eatoms={" << gsep;
00154         printer.printmany(innerEatoms,isep);
00155         o << gsep << "}" << gsep;
00156     }
00157     if( !gidb.empty() ) {
00158         o << "gidb={" << gsep;
00159         printer.printmany(gidb,isep);
00160         o << gsep << "}" << gsep;
00161     }
00162     if( !idb.empty() ) {
00163         o << "idb={" << gsep;
00164         printer.printmany(idb,isep);
00165         o << gsep << "}" << gsep;
00166     }
00167     if( !xidb.empty() ) {
00168         o << "xidb={" << gsep;
00169         printer.printmany(xidb,isep);
00170         o << gsep << "}" << gsep;
00171     }
00172     if( !xidbflphead.empty() ) {
00173         o << "xidbflphead={" << gsep;
00174         printer.printmany(xidbflphead,isep);
00175         o << gsep << "}" << gsep;
00176     }
00177     if( !xidbflpbody.empty() ) {
00178         o << "xidbflpbody={" << gsep;
00179         printer.printmany(xidbflpbody,isep);
00180         o << gsep << "}" << gsep;
00181     }
00182     return o;
00183 }
00184 
00185 
00186 //
00187 // the model generator
00188 //
00189 
00190 GenuineGuessAndCheckModelGenerator::GenuineGuessAndCheckModelGenerator(
00191 Factory& factory,
00192 InterpretationConstPtr input):
00193 FLPModelGeneratorBase(factory, input),
00194 factory(factory),
00195 reg(factory.reg),
00196 cmModelCount(0),
00197 haveInconsistencyCause(false)
00198 {
00199     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidconstruct, "genuine g&c mg constructor");
00200     DBGLOG(DBG, "Genuine GnC-ModelGenerator is instantiated for a " << (factory.ci.disjunctiveHeads ? "" : "non-") << "disjunctive component");
00201 
00202     RegistryPtr reg = factory.reg;
00203 
00204     // create new interpretation as copy
00205     InterpretationPtr postprocInput;
00206     if( input == 0 ) {
00207         // empty construction
00208         postprocInput.reset(new Interpretation(reg));
00209     }
00210     else {
00211         // copy construction
00212         postprocInput.reset(new Interpretation(*input));
00213     }
00214 
00215     // augment input with edb
00216     WARNING("perhaps we can pass multiple partially preprocessed input edb's to the external solver and save a lot of processing here")
00217         postprocInput->add(*factory.ctx.edb);
00218 
00219     // remember which facts we must remove
00220     mask.reset(new Interpretation(*postprocInput));
00221 
00222     // manage outer external atoms
00223     if( !factory.outerEatoms.empty() ) {
00224         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexground, "HEX grounder out EA GenGnCMG");
00225 
00226         // augment input with result of external atom evaluation
00227         // use newint as input and as output interpretation
00228         IntegrateExternalAnswerIntoInterpretationCB cb(postprocInput);
00229         evaluateExternalAtoms(factory.ctx,
00230             factory.outerEatoms, postprocInput, cb);
00231         DLVHEX_BENCHMARK_REGISTER(sidcountexternalatomcomps,
00232             "outer eatom computations");
00233         DLVHEX_BENCHMARK_COUNT(sidcountexternalatomcomps,1);
00234     }
00235 
00236     // compute extensions of domain predicates and add it to the input
00237     if (factory.ctx.config.getOption("LiberalSafety")) {
00238         InterpretationConstPtr domPredictaesExtension = computeExtensionOfDomainPredicates(factory.ci, factory.ctx, postprocInput, factory.deidb, factory.deidbInnerEatoms);
00239         postprocInput->add(*domPredictaesExtension);
00240     }
00241 
00242     // assign to const member -> this value must stay the same from here on!
00243     postprocessedInput = postprocInput;
00244 
00245     // evaluate edb+xidb+gidb
00246     {
00247         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"genuine g&c init guessprog");
00248         DBGLOG(DBG,"evaluating guessing program");
00249         // no mask
00250         OrdinaryASPProgram program(reg, factory.xidb, postprocessedInput, factory.ctx.maxint);
00251         // append gidb to xidb
00252         program.idb.insert(program.idb.end(), factory.gidb.begin(), factory.gidb.end());
00253 
00254         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexground, "HEX grounder GuessPr GenGnCMG");
00255         
00256         // run solver possibly with the possibility to analyze unit inconsistency at a later point
00257         if (factory.ctx.config.getOption("UnitInconsistencyAnalysis")){ initializeExplanationAtoms(program); }
00258 
00259         grounder = GenuineGrounder::getInstance(factory.ctx, program, explAtoms);
00260         OrdinaryASPProgram gp = grounder->getGroundProgram();
00261         // do not project within the solver as auxiliaries might be relevant for UFS checking (projection is done in G&C mg)
00262         if (!!gp.mask) mask->add(*gp.mask);
00263         gp.mask = InterpretationConstPtr();
00264         annotatedGroundProgram = AnnotatedGroundProgram(factory.ctx, gp, factory.innerEatoms);
00265 
00266         // external source inlining
00267         if (factory.ctx.config.getOption("ExternalSourceInlining")) {
00268             inlineExternalAtoms(gp, grounder, annotatedGroundProgram, activeInnerEatoms);
00269         }else{
00270             activeInnerEatoms = factory.innerEatoms;
00271         }
00272 
00273         // run solver
00274         solver = GenuineGroundSolver::getInstance(
00275             factory.ctx, annotatedGroundProgram,
00276             explAtoms,
00277             // do the UFS check for disjunctions only if we don't do
00278             // a minimality check in this class;
00279             // this will not find unfounded sets due to external sources,
00280             // but at least unfounded sets due to disjunctions
00281             !factory.ctx.config.getOption("FLPCheck") && !factory.ctx.config.getOption("UFSCheck"));
00282 
00283         // initialize a non-optimized solver instance for later inconsistency analysis (if requested)
00284         if (factory.ctx.config.getOption("UnitInconsistencyAnalysis")){ initializeInconsistencyAnalysis(); }
00285     }
00286 
00287     // external learning related initialization
00288     learnedEANogoods = SimpleNogoodContainerPtr(new SimpleNogoodContainer());
00289     learnedEANogoodsTransferredIndex = 0;
00290     nogoodGrounder = NogoodGrounderPtr(new ImmediateNogoodGrounder(factory.ctx.registry(), learnedEANogoods, learnedEANogoods, annotatedGroundProgram));
00291     if(factory.ctx.config.getOption("NoPropagator") == 0) {
00292         DBGLOG(DBG, "Adding propagator to solver");
00293         solver->addPropagator(this);
00294     }
00295     learnSupportSets();
00296 
00297     // external atom evaluation and unfounded set checking
00298     //   initialize UFS checker
00299     //     Concerning the last parameter, note that clasp backend uses choice rules for implementing disjunctions:
00300     //     this must be regarded in UFS checking (see examples/trickyufs.hex)
00301     ufscm = UnfoundedSetCheckerManagerPtr(new UnfoundedSetCheckerManager(*this, factory.ctx, annotatedGroundProgram,
00302         factory.ctx.config.getOption("GenuineSolver") >= 3,
00303         factory.ctx.config.getOption("ExternalLearning") ? learnedEANogoods : SimpleNogoodContainerPtr()));
00304 
00305     initializeHeuristics();
00306     initializeVerificationWatchLists();
00307 }
00308 
00309 GenuineGuessAndCheckModelGenerator::~GenuineGuessAndCheckModelGenerator()
00310 {
00311     DBGLOG(DBG, "Removing propagator to solver");
00312     solver->removePropagator(this);
00313     DBGLOG(DBG, "Final Statistics:" << std::endl << solver->getStatistics());
00314 }
00315 
00316 void GenuineGuessAndCheckModelGenerator::inlineExternalAtoms(OrdinaryASPProgram& program, GenuineGrounderPtr& grounder, AnnotatedGroundProgram& annotatedGroundProgram, std::vector<ID>& activeInnerEatoms) {
00317 
00318 #ifndef NDEBUG
00319         DBGLOG(DBG, "Inlining in program:" << std::endl << *program.edb << std::endl)
00320         BOOST_FOREACH (ID rID, program.idb) {
00321             DBGLOG(DBG, printToString<RawPrinter>(rID, reg));
00322         }
00323 #endif
00324 
00325     InterpretationPtr eliminatedExtAuxes(new Interpretation(reg));
00326     for(unsigned eaIndex = 0; eaIndex < factory.innerEatoms.size(); ++eaIndex) {
00327         // evaluate the external atom if it provides support sets
00328         const ExternalAtom& eatom = reg->eatoms.getByID(factory.innerEatoms[eaIndex]);
00329         if (eatom.getExtSourceProperties().providesSupportSets()) {
00330             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidinlined, "Inlined external atoms");
00331 
00332             DBGLOG(DBG, "Learning support sets for " << printToString<RawPrinter>(factory.innerEatoms[eaIndex], reg));
00333             SimpleNogoodContainerPtr supportSets = SimpleNogoodContainerPtr(new SimpleNogoodContainer());
00334             if (eatom.getExtSourceProperties().providesOnlySafeSupportSets()) {
00335                 learnSupportSetsForExternalAtom(factory.ctx, factory.innerEatoms[eaIndex], supportSets);
00336             }else{
00337                 SimpleNogoodContainerPtr potentialSupportSets = SimpleNogoodContainerPtr(new SimpleNogoodContainer());
00338                 learnSupportSetsForExternalAtom(factory.ctx, factory.innerEatoms[eaIndex], potentialSupportSets);
00339                 NogoodGrounderPtr nogoodgrounder = NogoodGrounderPtr(new ImmediateNogoodGrounder(factory.ctx.registry(), potentialSupportSets, potentialSupportSets, annotatedGroundProgram));
00340                 int nc = 0;
00341                 while (nc < potentialSupportSets->getNogoodCount()) {
00342                     nc = potentialSupportSets->getNogoodCount();
00343                     nogoodgrounder->update();
00344                 }
00345 
00346                 bool keep;
00347                 for (int i = 0; i < potentialSupportSets->getNogoodCount(); ++i) {
00348                     const Nogood& ng = potentialSupportSets->getNogood(i);
00349                     if (ng.isGround()) {
00350                         // determine the external atom replacement in ng
00351                         ID eaAux = ID_FAIL;
00352                         BOOST_FOREACH (ID lit, ng) {
00353                             if (reg->ogatoms.getIDByAddress(lit.address).isExternalAuxiliary()) {
00354                                 if (eaAux != ID_FAIL) throw GeneralError("Set " + ng.getStringRepresentation(reg) + " is not a valid support set because it contains multiple external literals");
00355                                 eaAux = lit;
00356                             }
00357                         }
00358                         if (eaAux == ID_FAIL) throw GeneralError("Set " + ng.getStringRepresentation(reg) + " is not a valid support set because it contains no external literals");
00359 
00360                         // determine the according external atom
00361                         if (annotatedGroundProgram.mapsAux(eaAux.address)) {
00362                             DBGLOG(DBG, "Evaluating guards of " << ng.getStringRepresentation(reg));
00363                             keep = true;
00364                             Nogood ng2 = ng;
00365                             reg->eatoms.getByID(annotatedGroundProgram.getAuxToEA(eaAux.address)[0]).pluginAtom->guardSupportSet(keep, ng2, eaAux);
00366                             if (keep) {
00367                                 #ifndef NDEBUG
00368                                 // ng2 must be a subset of ng and still a valid support set
00369                                 ID aux = ID_FAIL;
00370                                 BOOST_FOREACH (ID id, ng2) {
00371                                     if (reg->ogatoms.getIDByAddress(id.address).isExternalAuxiliary()) aux = id;
00372                                     assert(ng.count(id) > 0);
00373                                 }
00374                                 assert(aux != ID_FAIL);
00375                                 #endif
00376                                 DBGLOG(DBG, "Keeping in form " << ng2.getStringRepresentation(reg));
00377                                 supportSets->addNogood(ng2);
00378                                 #ifdef DEBUG
00379                             }
00380                             else {
00381                                 assert(ng == ng2);
00382                                 DBGLOG(DBG, "Rejecting " << ng2.getStringRepresentation(reg));
00383                                 #endif
00384                             }
00385                         }
00386                     }
00387                 }
00388             }
00389 
00390             // external atom support rules
00391             DBGLOG(DBG, "Constructing support rules for " << printToString<RawPrinter>(factory.innerEatoms[eaIndex], reg));
00392             for (int i = 0; i < supportSets->getNogoodCount(); ++i) {
00393                 const Nogood& ng = supportSets->getNogood(i);
00394                 DBGLOG(DBG, "Processing support set " << ng.getStringRepresentation(reg));
00395                 Rule supportRule(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR);
00396 
00397                 // find unique auxiliary and create head of support rule
00398                 ID auxID = ID_FAIL;
00399                 BOOST_FOREACH (ID lit, ng){
00400                     ID flit = (lit.isOrdinaryGroundAtom() ? reg->ogatoms.getIDByAddress(lit.address) : reg->onatoms.getIDByAddress(lit.address));
00401                     if (flit.isExternalAuxiliary()) {
00402                         if (reg->isNegativeExternalAtomAuxiliaryAtom(flit)) {
00403                             flit = reg->swapExternalAtomAuxiliaryAtom(flit);
00404                             flit.kind &= (ID::ALL_ONES ^ ID::NAF_MASK);
00405                         }
00406                         if (auxID != ID_FAIL) throw GeneralError("Invalid support set detected (contains multiple auxiliaries)");
00407                         auxID = flit;
00408                         supportRule.head.push_back(flit);
00409                     }
00410                 }
00411                 if (auxID == ID_FAIL) throw GeneralError("Invalid support set detected (contains no auxiliary)");
00412                 const OrdinaryAtom& aux = reg->lookupOrdinaryAtom(auxID);
00413 
00414                 // create body of support rule
00415                 BOOST_FOREACH (ID lit, ng){
00416                     ID flit = (lit.isOrdinaryGroundAtom() ? reg->ogatoms.getIDByAddress(lit.address) : reg->onatoms.getIDByAddress(lit.address));
00417                     flit.kind |= (lit.kind & ID::NAF_MASK);
00418                     if (!flit.isExternalAuxiliary()) {
00419                         // replace default-negated body atoms by the atoms which explicitly represent falsehood
00420                         if (flit.isNaf()){
00421                             const OrdinaryAtom& a = reg->ogatoms.getByID(flit);
00422                             OrdinaryAtom negA(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX);
00423                             negA.tuple = aux.tuple;
00424                             negA.tuple[0] = reg->getAuxiliaryConstantSymbol('F', aux.tuple[0]);
00425                             negA.tuple.insert(negA.tuple.end(), a.tuple.begin(), a.tuple.end());
00426                             ID negAID = reg->storeOrdinaryAtom(negA);
00427                             supportRule.body.push_back(negAID);
00428                         }else{
00429                             supportRule.body.push_back(flit);
00430                         }
00431                     }
00432                 }
00433 
00434                 // add support rule
00435                 ID supportRuleID = reg->storeRule(supportRule);
00436                 DBGLOG(DBG, "Adding support rule " << printToString<RawPrinter>(supportRuleID, reg));
00437                 program.idb.push_back(supportRuleID);
00438             }
00439 
00440             // explicit representation of negative input atoms
00441             typedef boost::unordered_map<IDAddress, std::vector<ID> > AuxToEAType;
00442             const AuxToEAType& auxToEA = annotatedGroundProgram.getAuxToEA();
00443             BOOST_FOREACH (AuxToEAType::value_type currentAux, auxToEA) {
00444                 ID auxID = reg->ogatoms.getIDByAddress(currentAux.first);
00445                 DBGLOG(DBG, "Processing external atom auxiliary " << printToString<RawPrinter>(auxID, reg));
00446                 eliminatedExtAuxes->setFact(auxID.address);
00447                 eliminatedExtAuxes->setFact(reg->swapExternalAtomAuxiliaryAtom(auxID).address);
00448                 const OrdinaryAtom& aux = reg->ogatoms.getByAddress(currentAux.first);
00449                 if (reg->getTypeByAuxiliaryConstantSymbol(aux.tuple[0]) == 'r') {
00450                     // for all input atoms
00451                     int eaIndex = annotatedGroundProgram.getIndexOfEAtom(currentAux.second[0]);
00452                     const boost::shared_ptr<ExternalAtomMask> mask = annotatedGroundProgram.getEAMask(eaIndex);
00453                     bm::bvector<>::enumerator en = mask->mask()->getStorage().first();
00454                     bm::bvector<>::enumerator en_end = mask->mask()->getStorage().end();
00455                     while (en < en_end) {
00456                         // atom "a"
00457                         ID aID = reg->ogatoms.getIDByAddress(*en);
00458                         if (!aID.isAuxiliary()) { // only input atoms from predicate input
00459                             const OrdinaryAtom& a = reg->ogatoms.getByID(aID);
00460                             DBGLOG(DBG, "Processing input atom: " << printToString<RawPrinter>(aID, reg));
00461 
00462                             // create an atom "af" which represents the negation of "a" when input to "aux"
00463                             OrdinaryAtom negA(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX);
00464                             negA.tuple = aux.tuple;
00465                             negA.tuple[0] = reg->getAuxiliaryConstantSymbol('F', aux.tuple[0]);
00466                             negA.tuple.insert(negA.tuple.end(), a.tuple.begin(), a.tuple.end());
00467                             ID negAID = reg->storeOrdinaryAtom(negA);
00468                             DBGLOG(DBG, "Negated atom: " << printToString<RawPrinter>(negAID, reg));
00469 
00470                             // "af" is true if "a" is false
00471                             Rule falsehoodRule(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR);
00472                             falsehoodRule.head.push_back(negAID);
00473                             falsehoodRule.body.push_back(ID::nafLiteralFromAtom(aID));
00474                             ID falsehoodRuleID = reg->storeRule(falsehoodRule);
00475                             DBGLOG(DBG, "Falsehood rule for input atom: " << printToString<RawPrinter>(falsehoodRuleID, reg));
00476                             program.idb.push_back(falsehoodRuleID);
00477 
00478                             // "af" is also true if "aux" is true
00479                             Rule saturationRule(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR);
00480                             saturationRule.head.push_back(negAID);
00481                             saturationRule.body.push_back(auxID);
00482                             ID saturationRuleID = reg->storeRule(saturationRule);
00483                             DBGLOG(DBG, "Saturation rule: " << printToString<RawPrinter>(saturationRuleID, reg));
00484                             program.idb.push_back(saturationRuleID);
00485 
00486                             // one of "a" or "af" must be true whenever "naux" is not false
00487                             Rule guessAorAF(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR | ID::PROPERTY_RULE_DISJ);
00488                             guessAorAF.head.push_back(aID);
00489                             guessAorAF.head.push_back(negAID);
00490                             guessAorAF.body.push_back(ID::nafLiteralFromAtom(reg->swapExternalAtomAuxiliaryAtom(auxID)));
00491                             ID guessAorAFID = reg->storeRule(guessAorAF);
00492                             DBGLOG(DBG, "Guessing rule: " << printToString<RawPrinter>(guessAorAFID, reg));
00493                             program.idb.push_back(guessAorAFID);
00494                         }
00495 
00496                         en++;
00497                     }
00498                 }
00499             }
00500         }else{
00501             activeInnerEatoms.push_back(factory.innerEatoms[eaIndex]);
00502         }
00503     }
00504 
00505     // 1. substitute external atom guessing rule "e v ne :- B" by "ne :- not a"
00506     // 2. replace external atom auxiliaries 'r'/'n' by 'R'/'N' in all rules
00507     DBGLOG(DBG, "Replacing external atom auxiliaries");
00508     OrdinaryASPProgram inlinedProgram(reg, std::vector<ID>(), program.edb, factory.ctx.maxint);
00509     for (int rIndex = 0; rIndex < program.idb.size(); ++rIndex) {
00510         DBGLOG(DBG, "Processing rule " << printToString<RawPrinter>(program.idb[rIndex], reg));
00511         const Rule& rule = reg->rules.getByID(program.idb[rIndex]);
00512         if (rule.isEAGuessingRule() && eliminatedExtAuxes->getFact(rule.head[0].address)){
00513             int posIndex = (reg->getTypeByAuxiliaryConstantSymbol(rule.head[0]) == 'r' ? 0 : 1);
00514             Rule simplifiedGuessingRule(ID::MAINKIND_RULE | ID::SUBKIND_RULE_REGULAR);
00515             simplifiedGuessingRule.head.push_back(replacePredForInlinedEAs(rule.head[1 - posIndex], eliminatedExtAuxes));
00516             simplifiedGuessingRule.body = rule.body;
00517             simplifiedGuessingRule.body.push_back(ID::nafLiteralFromAtom(replacePredForInlinedEAs(rule.head[posIndex], eliminatedExtAuxes)));
00518             ID simplifiedGuessingRuleID = reg->storeRule(simplifiedGuessingRule);
00519             DBGLOG(DBG, "Simplified guessing rule " << printToString<RawPrinter>(program.idb[rIndex], reg) << " to " << printToString<RawPrinter>(simplifiedGuessingRuleID, reg));
00520             inlinedProgram.idb.push_back(simplifiedGuessingRuleID);
00521         }else{
00522             Rule* newRule = 0;  // in most cases we do not actually need to create one (if it is the same as the existing one)
00523 
00524             // substitute in all head atoms
00525             for (int hIndex = 0; hIndex < rule.head.size(); ++hIndex) {
00526                 ID newAtomID = replacePredForInlinedEAs(rule.head[hIndex], eliminatedExtAuxes);
00527                 if (newAtomID != rule.head[hIndex]) {
00528                     if (!newRule) {
00529                         newRule = new Rule(rule);
00530                     }
00531                     newRule->head[hIndex] = newAtomID;
00532                 }
00533             }
00534 
00535             // substitute in all body atoms
00536             for (int bIndex = 0; bIndex < rule.body.size(); ++bIndex) {
00537                 ID newAtomID = replacePredForInlinedEAs(rule.body[bIndex], eliminatedExtAuxes);
00538                 if (newAtomID != rule.body[bIndex]) {
00539                     if (!newRule) {
00540                         newRule = new Rule(rule);
00541                     }
00542                     newRule->body[bIndex] = (newRule->body[bIndex].isNaf() ? ID::nafLiteralFromAtom(newAtomID) : ID::posLiteralFromAtom(newAtomID));
00543                 }
00544             }
00545             // was the rule modified?
00546             if (!!newRule){
00547                 inlinedProgram.idb.push_back(reg->storeRule(*newRule));
00548                 delete newRule;
00549             }else{
00550                 inlinedProgram.idb.push_back(program.idb[rIndex]);
00551             }
00552         }
00553     }
00554 
00555 #ifndef NDEBUG
00556     DBGLOG(DBG, "Inlined program:" << std::endl << *inlinedProgram.edb << std::endl);
00557     BOOST_FOREACH (ID rID, inlinedProgram.idb) {
00558         DBGLOG(DBG, printToString<RawPrinter>(rID, reg));
00559     }
00560 #endif
00561 
00562     // reground and reanalyze extended program
00563     grounder = GenuineGrounder::getInstance(factory.ctx, inlinedProgram, explAtoms);
00564     OrdinaryASPProgram gp = grounder->getGroundProgram();
00565     // do not project within the solver as auxiliaries might be relevant for UFS checking (projection is done in G&C mg)
00566     if (!!gp.mask) mask->add(*gp.mask);
00567     gp.mask = InterpretationConstPtr();
00568     annotatedGroundProgram = AnnotatedGroundProgram(factory.ctx, gp, activeInnerEatoms);
00569 }
00570 
00571 ID GenuineGuessAndCheckModelGenerator::replacePredForInlinedEAs(ID atomID, InterpretationConstPtr eliminatedExtAuxes) {
00572 
00573     DBGLOG(DBG, "replacePredForInlinedEAs called for " << printToString<RawPrinter>(atomID, factory.ctx.registry()));
00574 
00575     // only external predicates are inlined
00576     if (!atomID.isExternalAuxiliary() || !eliminatedExtAuxes->getFact(atomID.address)) {
00577         DBGLOG(DBG, "--> not an eliminated external atom auxiliary; aborting");
00578         return atomID;
00579     }
00580 
00581     const OrdinaryAtom& oatom = factory.ctx.registry()->lookupOrdinaryAtom(atomID);
00582     char type = reg->getTypeByAuxiliaryConstantSymbol(oatom.tuple[0]);
00583     ID id = reg->getIDByAuxiliaryConstantSymbol(oatom.tuple[0]);
00584     DBGLOG(DBG, "replacePredForInlinedEAs: type=" << type << "; id=" << id << " (" << printToString<RawPrinter>(id, reg) << ")");
00585     if ((type == 'r' || type == 'n')) {
00586         // change 'r' to 'R' and 'n' to 'N'
00587         type -= 32;
00588         OrdinaryAtom inlined = oatom;
00589         inlined.kind &= (ID::ALL_ONES ^ ID::PROPERTY_EXTERNALAUX);
00590         inlined.tuple[0] = reg->getAuxiliaryConstantSymbol(type, id);
00591         ID newID = reg->storeOrdinaryAtom(inlined);
00592         DBGLOG(DBG, "replacePredForInlinedEAs returns " << printToString<RawPrinter>(newID, factory.ctx.registry()));
00593         return newID;
00594     }
00595 }
00596 
00597 void GenuineGuessAndCheckModelGenerator::initializeExplanationAtoms(OrdinaryASPProgram& program){
00598 
00599     // Explanation atoms are all ground atoms from the registry which
00600     // (i) use a predicate which occurs in this unit (either in an ordinary atom or in an external atom input list); and
00601     // (ii) are not defined in this unit.
00602     // This captures exactly the atoms which *could* be derivable in some predecessor unit.
00603     // Atoms from successor and sibling units are excluded by Condition (i) (they cannot occur in this unit because evaluation graphs are acyclic).
00604     // Atoms from this unit are excluded by Condition (ii).
00605     PredicateMaskPtr explAtomMask(new PredicateMask());
00606     explAtoms.reset(new Interpretation(factory.ctx.registry()));
00607     explAtomMask->setRegistry(factory.ctx.registry());
00608     DBGLOG(DBG, "Computing set of explanation atoms");
00609     if (factory.ctx.config.getOption("UnitInconsistencyAnalysisDebug")) {   // in debug mode we analyze conflicts wrt. "explain" atoms rather than the unit input
00610         explAtomMask->addPredicate(factory.ctx.registry()->storeConstantTerm("explain"));
00611     }else{
00612         BOOST_FOREACH (ID predInComp, factory.ci.predicatesOccurringInComponent) {
00613             DBGLOG(DBG, "Predicate " << printToString<RawPrinter>(predInComp, factory.ctx.registry()) << " occurs in unit");
00614             if (factory.ci.predicatesDefinedInComponent.find(predInComp) == factory.ci.predicatesDefinedInComponent.end()) {
00615                 DBGLOG(DBG, "Predicate " << printToString<RawPrinter>(predInComp, factory.ctx.registry()) << " is not defined in unit --> explanation atom");
00616                 explAtomMask->addPredicate(predInComp);
00617             }
00618         }
00619         // nogoods learned from other components belong to this unit
00620         typedef std::pair<Nogood, int> NogoodIntegerPair;
00621         BOOST_FOREACH (NogoodIntegerPair nip, factory.succNogoods){
00622             BOOST_FOREACH (ID atom, nip.first){
00623                 ID predInComp = factory.ctx.registry()->ogatoms.getByID(atom).tuple[0];
00624                 if (factory.ci.predicatesDefinedInComponent.find(predInComp) == factory.ci.predicatesDefinedInComponent.end()) {
00625                     DBGLOG(DBG, "Predicate " << printToString<RawPrinter>(predInComp, factory.ctx.registry()) << " is not defined in unit --> explanation atom");
00626                     explAtomMask->addPredicate(predInComp);
00627                 }
00628             }
00629         }
00630     }
00631     explAtomMask->updateMask();
00632     explAtoms->getStorage() |= explAtomMask->mask()->getStorage();
00633     //explAtoms->getStorage() |= input->getStorage();
00634     DBGLOG(DBG, "Explanation atoms for inconsistency analysis: " << *explAtoms);
00635 
00636     if (!!explAtoms){
00637         InterpretationPtr edbWithoutExplAtoms(new Interpretation(*postprocessedInput));
00638         edbWithoutExplAtoms->getStorage() -= explAtoms->getStorage();
00639         program.edb = edbWithoutExplAtoms;
00640     }
00641 }
00642 
00643 void GenuineGuessAndCheckModelGenerator::initializeInconsistencyAnalysis(){
00644 
00645     // explanation atoms are assumptions
00646     if (!!explAtoms){
00647         std::vector<ID> assumptions;
00648         bm::bvector<>::enumerator en = explAtoms->getStorage().first();
00649         bm::bvector<>::enumerator en_end = explAtoms->getStorage().end();
00650         while (en < en_end) {
00651             assumptions.push_back(postprocessedInput->getFact(*en) ? ID::posLiteralFromAtom(factory.ctx.registry()->ogatoms.getIDByAddress(*en)) : ID::nafLiteralFromAtom(factory.ctx.registry()->ogatoms.getIDByAddress(*en)));
00652             DBGLOG(DBG, "Adding assumption " << printToString<RawPrinter>(assumptions[assumptions.size() - 1], factory.ctx.registry()));
00653             en++;
00654         }
00655 
00656         // restart standard solver with assumptions
00657         solver->restartWithAssumptions(assumptions);
00658 
00659         // start analysis solver
00660         analysissolver.reset(new InternalGroundDASPSolver(factory.ctx, annotatedGroundProgram, explAtoms));
00661         analysissolver->restartWithAssumptions(assumptions);
00662     }
00663 
00664     // update nogoods learned from successor (add all ground atoms which have been added to the registry in the meantime in negative form) and add it to the new model generator
00665     if (factory.ctx.config.getOption("TransUnitLearning")){
00666         typedef std::pair<Nogood, int> NogoodIntegerPair;
00667         BOOST_FOREACH (NogoodIntegerPair nip, factory.succNogoods){
00668             for (int i = nip.second; i < factory.ctx.registry()->ogatoms.getSize(); i++){
00669                 if (annotatedGroundProgram.getProgramMask()->getFact(i)) nip.first.insert(NogoodContainer::createLiteral(i, false));
00670             }
00671             nip.second = factory.ctx.registry()->ogatoms.getSize();
00672             addNogood(&nip.first);
00673         }
00674     }
00675 }
00676 
00677 void GenuineGuessAndCheckModelGenerator::initializeHeuristics()
00678 {
00679 
00680     defaultExternalAtomEvalHeuristics = factory.ctx.defaultExternalAtomEvaluationHeuristicsFactory->createHeuristics(reg);
00681 
00682     // set external atom evaluation strategy according to selected heuristics
00683     for (uint32_t i = 0; i < activeInnerEatoms.size(); ++i) {
00684         const ExternalAtom& eatom = reg->eatoms.getByID(activeInnerEatoms[i]);
00685 
00686         eaEvaluated.push_back(false);
00687         eaVerified.push_back(false);
00688         changedAtomsPerExternalAtom.push_back(eatom.getExtSourceProperties().doesCareAboutChanged() ? InterpretationPtr(new Interpretation(reg)) : InterpretationPtr());
00689 
00690         // custom or default heuristics?
00691         if (eatom.pluginAtom->providesCustomExternalAtomEvaluationHeuristicsFactory()) {
00692             DBGLOG(DBG, "Using custom external atom heuristics for external atom " << activeInnerEatoms[i]);
00693             eaEvalHeuristics.push_back(eatom.pluginAtom->getCustomExternalAtomEvaluationHeuristicsFactory()->createHeuristics(reg));
00694         }
00695         else {
00696             DBGLOG(DBG, "Using default external atom heuristics for external atom " << activeInnerEatoms[i]);
00697             eaEvalHeuristics.push_back(defaultExternalAtomEvalHeuristics);
00698         }
00699     }
00700 
00701     // create ufs check heuristics as selected
00702     ufsCheckHeuristics = factory.ctx.unfoundedSetCheckHeuristicsFactory->createHeuristics(annotatedGroundProgram, reg);
00703     verifiedAuxes = InterpretationPtr(new Interpretation(reg));
00704 }
00705 
00706 
00707 void GenuineGuessAndCheckModelGenerator::initializeVerificationWatchLists()
00708 {
00709 
00710     // set external atom evaluation strategy according to selected heuristics
00711     verifyWatchList.clear();
00712     unverifyWatchList.clear();
00713     for (uint32_t i = 0; i < activeInnerEatoms.size(); ++i) {
00714 
00715         // watch all atoms in the scope of the external atom for watch one input atom for verification
00716         bm::bvector<>::enumerator en = annotatedGroundProgram.getEAMask(i)->mask()->getStorage().first();
00717         bm::bvector<>::enumerator en_end = annotatedGroundProgram.getEAMask(i)->mask()->getStorage().end();
00718         if (en < en_end) {
00719             verifyWatchList[*en].push_back(i);
00720         }
00721 
00722         // watch all atoms in the scope of the external atom for unverification
00723         en = annotatedGroundProgram.getEAMask(i)->mask()->getStorage().first();
00724         en_end = annotatedGroundProgram.getEAMask(i)->mask()->getStorage().end();
00725         while (en < en_end) {
00726             unverifyWatchList[*en].push_back(i);
00727             en++;
00728         }
00729     }
00730 }
00731 
00732 
00733 InterpretationPtr GenuineGuessAndCheckModelGenerator::generateNextModel()
00734 {
00735     // now we have postprocessed input in postprocessedInput
00736     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidgcsolve, "genuine guess and check loop");
00737     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve, "HEX solver time (gNM GenGnC)");
00738     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexsolve2, "HEX solver time");
00739 
00740     InterpretationPtr modelCandidate;
00741     do {
00742         LOG(DBG,"asking for next model");
00743 
00744         // Search space pruning: the idea is to set the current global optimum as upper limit in the solver instance (of this unit) to eliminate interpretations with higher costs.
00745         // Note that this optimization is conservative such that the algorithm remains complete even when the program is split. Because costs can be only positive,
00746         // if the costs of a partial model are greater than the current global optimum then also any completion of this partial model (by combining it with other units)
00747         // would be non-optimal.
00748         if (factory.ctx.config.getOption("OptimizationByBackend")) solver->setOptimum(factory.ctx.currentOptimum);
00749         modelCandidate = solver->getNextModel();
00750 
00751         // test inconsistency explanations
00752         if (!modelCandidate && factory.ctx.config.getOption("UnitInconsistencyAnalysis") && !!explAtoms && cmModelCount == 0) { identifyInconsistencyCause(); }
00753 
00754         DBGLOG(DBG, "Statistics:" << std::endl << solver->getStatistics());
00755         if( !modelCandidate ) {
00756             LOG(DBG,"unsatisfiable -> returning no model");
00757             return InterpretationPtr();
00758         }
00759 
00760         DLVHEX_BENCHMARK_REGISTER_AND_COUNT(ssidmodelcandidates, "Candidate compatible sets", 1);
00761         LOG_SCOPE(DBG,"gM", false);
00762         LOG(DBG,"got guess model, will do compatibility check on " << *modelCandidate);
00763         if (!finalCompatibilityCheck(modelCandidate)) {
00764             LOG(DBG,"compatibility failed");
00765             if (!!analysissolver){
00766                 InterpretationPtr rematoms(new Interpretation(factory.ctx.registry()));
00767                 InterpretationPtr relevantatoms(new Interpretation(*annotatedGroundProgram.getProgramMask()));
00768                 relevantatoms->getStorage() |= postprocessedInput->getStorage();
00769                 bm::bvector<>::enumerator en = relevantatoms->getStorage().first();
00770                 bm::bvector<>::enumerator en_end = relevantatoms->getStorage().end();
00771                 while (en < en_end) {
00772                     if (factory.ctx.registry()->ogatoms.getIDByAddress(*en).isAuxiliary()) rematoms->setFact(*en);
00773                     en++;
00774                 }
00775                 relevantatoms->getStorage() -= rematoms->getStorage();
00776                 Nogood ng(relevantatoms, modelCandidate);
00777                 DBGLOG(DBG, "Adding model candidate " << ng.getStringRepresentation(factory.ctx.registry()) << " to inconsistency analyzer");
00778                 analysissolver->addNogood(ng);
00779             }
00780             continue;
00781         }
00782 
00783         LOG(DBG, "Checking if model candidate is a model");
00784         if (!isModel(modelCandidate)) {
00785             LOG(DBG,"isModel failed");
00786             continue;
00787         }
00788 
00789         // remove edb and the guess (from here we don't need the guess anymore)
00790         {
00791             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidcc, "GenuineGnCMG::gNM postproc");
00792             DBGLOG(DBG, "Got a model, removing replacement atoms");
00793             modelCandidate->getStorage() -= factory.gpMask.mask()->getStorage();
00794             modelCandidate->getStorage() -= factory.gnMask.mask()->getStorage();
00795             modelCandidate->getStorage() -= mask->getStorage();
00796         }
00797 
00798         LOG(DBG,"returning model without guess: " << *modelCandidate);
00799 
00800         cmModelCount++;
00801         return modelCandidate;
00802     }while(true);
00803 }
00804 
00805 void GenuineGuessAndCheckModelGenerator::identifyInconsistencyCause() {
00806 
00807     // imodel must always be NULL, but we still have to call analysissolver->getNextModel() to make sure that it propagates to derive the conflict
00808     InterpretationConstPtr imodel = analysissolver->getNextModel();
00809 #ifndef NDEBUG
00810     if (!!imodel) { DBGLOG(DBG, "Error: Inconsistency analysis program was model " << *imodel << " but should be inconsistent"); }
00811     assert (!imodel && "Instance did not yield models, but after restart it is not inconsistent!");
00812 #endif
00813     haveInconsistencyCause = true;
00814     inconsistencyCause = analysissolver->getInconsistencyCause(explAtoms);
00815     DBGLOG(DBG, "Inconsistency of program and inconsistence cause have been detected: " << inconsistencyCause.getStringRepresentation(factory.ctx.registry()));
00816 }
00817 
00818 const Nogood* GenuineGuessAndCheckModelGenerator::getInconsistencyCause(){
00819     DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sidic, "Unit inconsistency causes", (haveInconsistencyCause ? 1 : 0));
00820     DBGLOG(DBG, "Inconsistency cause was requested: " << (haveInconsistencyCause ? "" : "not") << " available");
00821     return (factory.ctx.config.getOption("TransUnitLearning") && haveInconsistencyCause ? &inconsistencyCause : 0);
00822 }
00823 
00824 void GenuineGuessAndCheckModelGenerator::addNogood(const Nogood* cause){
00825     DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sidna, "Nogoods added from outside to GnC mg", 1);
00826     DBGLOG(DBG, "Adding nogood to model generator: " << cause->getStringRepresentation(factory.ctx.registry()));
00827     if (factory.ctx.config.getOption("TransUnitLearning")){
00828         solver->addNogood(*cause);
00829         if (!!analysissolver) analysissolver->addNogood(*cause);
00830     }
00831 }
00832 
00833 void GenuineGuessAndCheckModelGenerator::generalizeNogood(Nogood ng)
00834 {
00835 
00836     if (!ng.isGround()) return;
00837 
00838     DBGLOG(DBG, "Generalizing " << ng.getStringRepresentation(reg));
00839 
00840     // find the external atom related to this nogood
00841     ID eaid = ID_FAIL;
00842     BOOST_FOREACH (ID l, ng) {
00843         if (reg->ogatoms.getIDByAddress(l.address).isExternalAuxiliary() && annotatedGroundProgram.mapsAux(l.address)) {
00844             eaid = l;
00845             break;
00846         }
00847     }
00848     if (eaid == ID_FAIL) return;
00849 
00850     assert(annotatedGroundProgram.getAuxToEA(eaid.address).size() > 0);
00851     DBGLOG(DBG, "External atom is " << annotatedGroundProgram.getAuxToEA(eaid.address)[0]);
00852     const ExternalAtom& ea = reg->eatoms.getByID(annotatedGroundProgram.getAuxToEA(eaid.address)[0]);
00853 
00854     // learn related nonground nogoods
00855     int oldCount = learnedEANogoods->getNogoodCount();
00856     ea.pluginAtom->generalizeNogood(ng, &factory.ctx, learnedEANogoods);
00857 }
00858 
00859 
00860 void GenuineGuessAndCheckModelGenerator::learnSupportSets()
00861 {
00862 
00863     if (factory.ctx.config.getOption("SupportSets")) {
00864         SimpleNogoodContainerPtr potentialSupportSets = SimpleNogoodContainerPtr(new SimpleNogoodContainer());
00865         SimpleNogoodContainerPtr supportSets = SimpleNogoodContainerPtr(new SimpleNogoodContainer());
00866         for(unsigned eaIndex = 0; eaIndex < activeInnerEatoms.size(); ++eaIndex) {
00867             // evaluate the external atom if it provides support sets
00868             const ExternalAtom& eatom = reg->eatoms.getByID(activeInnerEatoms[eaIndex]);
00869             if (eatom.getExtSourceProperties().providesSupportSets()) {
00870                 DBGLOG(DBG, "Evaluating external atom " << activeInnerEatoms[eaIndex] << " for support set learning");
00871                 learnSupportSetsForExternalAtom(factory.ctx, activeInnerEatoms[eaIndex], potentialSupportSets);
00872             }
00873         }
00874 
00875         DLVHEX_BENCHMARK_REGISTER(sidnongroundpsupportsets, "nonground potential supportsets");
00876         DLVHEX_BENCHMARK_COUNT(sidnongroundpsupportsets, potentialSupportSets->getNogoodCount());
00877 
00878         // ground the support sets exhaustively
00879         NogoodGrounderPtr nogoodgrounder = NogoodGrounderPtr(new ImmediateNogoodGrounder(factory.ctx.registry(), potentialSupportSets, potentialSupportSets, annotatedGroundProgram));
00880 
00881         int nc = 0;
00882         while (nc < potentialSupportSets->getNogoodCount()) {
00883             nc = potentialSupportSets->getNogoodCount();
00884             nogoodgrounder->update();
00885         }
00886         DLVHEX_BENCHMARK_REGISTER(sidgroundpsupportsets, "ground potential supportsets");
00887         DLVHEX_BENCHMARK_COUNT(sidgroundpsupportsets, supportSets->getNogoodCount());
00888 
00889         // all support sets are also learned nogoods
00890         bool keep;
00891         for (int i = 0; i < potentialSupportSets->getNogoodCount(); ++i) {
00892             const Nogood& ng = potentialSupportSets->getNogood(i);
00893             if (ng.isGround()) {
00894                 // determine the external atom replacement in ng
00895                 ID eaAux = ID_FAIL;
00896                 BOOST_FOREACH (ID lit, ng) {
00897                     if (reg->ogatoms.getIDByAddress(lit.address).isExternalAuxiliary()) {
00898                         if (eaAux != ID_FAIL) throw GeneralError("Set " + ng.getStringRepresentation(reg) + " is not a valid support set because it contains multiple external literals");
00899                         eaAux = lit;
00900                     }
00901                 }
00902                 if (eaAux == ID_FAIL) throw GeneralError("Set " + ng.getStringRepresentation(reg) + " is not a valid support set because it contains no external literals");
00903 
00904                 // determine the according external atom
00905                 if (annotatedGroundProgram.mapsAux(eaAux.address)) {
00906                     DBGLOG(DBG, "Evaluating guards of " << ng.getStringRepresentation(reg));
00907                     keep = true;
00908                     Nogood ng2 = ng;
00909                     reg->eatoms.getByID(annotatedGroundProgram.getAuxToEA(eaAux.address)[0]).pluginAtom->guardSupportSet(keep, ng2, eaAux);
00910                     if (keep) {
00911                         #ifdef DEBUG
00912                         // ng2 must be a subset of ng and still a valid support set
00913                         ID aux = ID_FAIL;
00914                         BOOST_FOREACH (ID id, ng2) {
00915                             if (reg->ogatoms.getIDByAddress(id.address).isExternalAuxiliary()) aux = id;
00916                             assert(ng.count(id) > 0);
00917                         }
00918                         assert(aux != ID_FAIL);
00919                         #endif
00920                         DBGLOG(DBG, "Keeping in form " << ng2.getStringRepresentation(reg));
00921                         learnedEANogoods->addNogood(ng2);
00922                         supportSets->addNogood(ng2);
00923                         #ifdef DEBUG
00924                     }
00925                     else {
00926                         assert(ng == ng2);
00927                         DBGLOG(DBG, "Rejecting " << ng2.getStringRepresentation(reg));
00928                         #endif
00929                     }
00930                 }
00931             }
00932         }
00933 
00934         DLVHEX_BENCHMARK_REGISTER(sidgroundsupportsets, "final ground supportsets");
00935         DLVHEX_BENCHMARK_COUNT(sidgroundsupportsets, supportSets->getNogoodCount());
00936 
00937         // add them to the annotated ground program to make use of them for verification
00938         DBGLOG(DBG, "Adding " << supportSets->getNogoodCount() << " support sets to annotated ground program");
00939         annotatedGroundProgram.setCompleteSupportSetsForVerification(supportSets);
00940     }
00941 }
00942 
00943 
00944 void GenuineGuessAndCheckModelGenerator::updateEANogoods(
00945 InterpretationConstPtr compatibleSet,
00946 InterpretationConstPtr assigned,
00947 InterpretationConstPtr changed)
00948 {
00949     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(siduean, "updateEANogoods");
00950     DBGLOG(DBG, "updateEANogoods");
00951 
00952     // generalize ground nogoods to nonground ones
00953     if (factory.ctx.config.getOption("ExternalLearningGeneralize")) {
00954         int max = learnedEANogoods->getNogoodCount();
00955         for (int i = learnedEANogoodsTransferredIndex; i < max; ++i) {
00956             generalizeNogood(learnedEANogoods->getNogood(i));
00957         }
00958     }
00959 
00960     // instantiate nonground nogoods
00961     if (factory.ctx.config.getOption("NongroundNogoodInstantiation")) {
00962         nogoodGrounder->update(compatibleSet, assigned, changed);
00963     }
00964 
00965     // transfer nogoods to the solver
00966     DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sidcompatiblesets, "Learned EA-Nogoods", learnedEANogoods->getNogoodCount() - learnedEANogoodsTransferredIndex);
00967     for (int i = learnedEANogoodsTransferredIndex; i < learnedEANogoods->getNogoodCount(); ++i) {
00968         const Nogood& ng = learnedEANogoods->getNogood(i);
00969         DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sidnogoodsizes, "Sum of learned EA-Nogood sizes", ng.size());
00970         if (factory.ctx.config.getOption("PrintLearnedNogoods")) {
00971             // we cannot use i==1 because of learnedEANogoods.clear() below in this function
00972             static bool first = true;
00973             if( first ) {
00974                 if (factory.ctx.config.getOption("GenuineSolver") >= 3) {
00975                     LOG(DBG, "( NOTE: With clasp backend, learned nogoods become effective with a delay because of multithreading! )");
00976                 }
00977                 else {
00978                     LOG(DBG, "( NOTE: With i-backend, learned nogoods become effective AFTER the next model was printed ! )");
00979                 }
00980                 first = false;
00981             }
00982             LOG(DBG,"learned nogood " << ng.getStringRepresentation(reg));
00983         }
00984         if (ng.isGround()) {
00985             DBGLOG(DBG, "Adding learned nogood " << ng.getStringRepresentation(reg) << " to solver");
00986             if (factory.ctx.config.getStringOption("DumpEANogoods")[0] != '\0'){
00987                 std::ofstream filev(factory.ctx.config.getStringOption("DumpEANogoods").c_str(), std::ios_base::app);
00988                 filev << ng.getStringRepresentation(reg) << std::endl;
00989             }
00990             solver->addNogood(ng);
00991             if (!!analysissolver) analysissolver->addNogood(ng);
00992 
00993             if ( factory.ctx.config.getOption("ExternalAtomVerificationFromLearnedNogoods") ) {
00994                 eavTree.addNogood(ng, reg, true);
00995                 DBGLOG(DBG, "Adding nogood " << ng.getStringRepresentation(reg) << "; to verification tree; updated tree:" << std::endl << eavTree.toString(reg));
00996             }
00997         }
00998     }
00999 
01000     // for encoding-based UFS checkers and explicit FLP checks, we need to keep learned nogoods (otherwise future UFS searches will not be able to use them)
01001     // for assumption-based UFS checkers we can delete them as soon as nogoods were added both to the main search and to the UFS search
01002     if ( factory.ctx.config.getOption("UFSCheckAssumptionBased") ||
01003          (annotatedGroundProgram.hasECycles() == 0 && factory.ctx.config.getOption("FLPDecisionCriterionE")) ) {
01004         ufscm->learnNogoodsFromMainSearch(true);
01005         if (factory.ctx.config.getOption("NongroundNogoodInstantiation")) nogoodGrounder->resetWatched(learnedEANogoods);
01006         learnedEANogoods->clear();
01007     }
01008     else {
01009         learnedEANogoods->forgetLeastFrequentlyAdded();
01010     }
01011     learnedEANogoodsTransferredIndex = learnedEANogoods->getNogoodCount();
01012 }
01013 
01014 
01015 bool GenuineGuessAndCheckModelGenerator::finalCompatibilityCheck(InterpretationConstPtr modelCandidate)
01016 {
01017 
01018     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidcc, "GenuineGnCMG: finalCompat");
01019     // did we already verify during model construction or do we have to do the verification now?
01020     bool compatible;
01021 
01022     compatible = true;
01023     for (uint32_t eaIndex = 0; eaIndex < activeInnerEatoms.size(); ++eaIndex) {
01024         DBGLOG(DBG, "NoPropagator: " << factory.ctx.config.getOption("NoPropagator") << ", eaEvaluated[" << eaIndex << "]=" << eaEvaluated[eaIndex]);
01025         assert(!(factory.ctx.config.getOption("NoPropagator") && eaEvaluated[eaIndex]) && "Verification result was stored for later usage although NoPropagator property was set");
01026         if (eaEvaluated[eaIndex] == true && eaVerified[eaIndex] == true) {
01027         }
01028         if (eaEvaluated[eaIndex] == true && eaVerified[eaIndex] == false) {
01029             DBGLOG(DBG, "External atom " << activeInnerEatoms[eaIndex] << " was evaluated but falsified");
01030             compatible = false;
01031             if (!factory.ctx.config.getOption("AlwaysEvaluateAllExternalAtoms")) break;
01032         }
01033         if (eaEvaluated[eaIndex] == false) {
01034             // try to verify
01035             DBGLOG(DBG, "External atom " << activeInnerEatoms[eaIndex] << " is not verified, trying to do this now");
01036             verifyExternalAtom(eaIndex, modelCandidate);
01037             DBGLOG(DBG, "Verification result: " << eaVerified[eaIndex]);
01038 
01039             if (eaVerified[eaIndex] == false) {
01040                 compatible = false;
01041                 if (!factory.ctx.config.getOption("AlwaysEvaluateAllExternalAtoms")) break;
01042             }
01043         }
01044     }
01045     DBGLOG(DBG, "Compatible: " << compatible);
01046 
01047     return compatible;
01048 }
01049 
01050 
01051 bool GenuineGuessAndCheckModelGenerator::isModel(InterpretationConstPtr compatibleSet)
01052 {
01053 
01054     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidcc, "GenuineGnCMG: isModel");
01055     // FLP: ensure minimality of the compatible set wrt. the reduct (if necessary)
01056     if (annotatedGroundProgram.hasHeadCycles() == 0 && annotatedGroundProgram.hasECycles() == 0 &&
01057     factory.ctx.config.getOption("FLPDecisionCriterionHead") && factory.ctx.config.getOption("FLPDecisionCriterionE")) {
01058         DBGLOG(DBG, "No head- or e-cycles --> No FLP/UFS check necessary");
01059         return true;
01060     }
01061     else {
01062         DBGLOG(DBG, "Head- or e-cycles --> FLP/UFS check necessary");
01063 
01064         // Explicit FLP check
01065         if (factory.ctx.config.getOption("FLPCheck")) {
01066             DBGLOG(DBG, "FLP Check");
01067             // do FLP check (possibly with nogood learning) and add the learned nogoods to the main search
01068             bool result = isSubsetMinimalFLPModel<GenuineSolver>(compatibleSet, postprocessedInput, factory.ctx,
01069                 factory.ctx.config.getOption("ExternalLearning") ? learnedEANogoods : SimpleNogoodContainerPtr());
01070             updateEANogoods(compatibleSet);
01071             return result;
01072         }
01073 
01074         // UFS check
01075         if (factory.ctx.config.getOption("UFSCheck")) {
01076             DBGLOG(DBG, "UFS Check");
01077             bool result = unfoundedSetCheck(compatibleSet);
01078             updateEANogoods(compatibleSet);
01079             return result;
01080         }
01081 
01082         // no check
01083         return true;
01084     }
01085 
01086     assert (false);
01087 }
01088 
01089 
01090 namespace
01091 {
01092     // collect all components on the way
01093     struct DFSVisitor:
01094     public boost::default_dfs_visitor
01095     {
01096         const ComponentGraph& cg;
01097         ComponentGraph::ComponentSet& comps;
01098         DFSVisitor(const ComponentGraph& cg, ComponentGraph::ComponentSet& comps): boost::default_dfs_visitor(), cg(cg), comps(comps) {}
01099         DFSVisitor(const DFSVisitor& other): boost::default_dfs_visitor(), cg(other.cg), comps(other.comps) {}
01100         template<typename GraphT>
01101         void discover_vertex(ComponentGraph::Component comp, const GraphT&) {
01102             comps.insert(comp);
01103         }
01104     };
01105 
01106     void transitivePredecessorComponents(const ComponentGraph& compgraph, ComponentGraph::Component from, ComponentGraph::ComponentSet& preds) {
01107         // we need a hash map, as component graph is no graph with vecS-storage
01108         //
01109         typedef boost::unordered_map<ComponentGraph::Component, boost::default_color_type> CompColorHashMap;
01110         typedef boost::associative_property_map<CompColorHashMap> CompColorMap;
01111         CompColorHashMap ccWhiteHashMap;
01112         // fill white hash map
01113         ComponentGraph::ComponentIterator cit, cit_end;
01114         for(boost::tie(cit, cit_end) = compgraph.getComponents();
01115         cit != cit_end; ++cit) {
01116             //boost::put(ccWhiteHashMap, *cit, boost::white_color);
01117             ccWhiteHashMap[*cit] = boost::white_color;
01118         }
01119         CompColorHashMap ccHashMap(ccWhiteHashMap);
01120 
01121         //
01122         // do DFS
01123         //
01124         DFSVisitor dfs_vis(compgraph, preds);
01125         //LOG("doing dfs visit for root " << *itr);
01126         boost::depth_first_visit(
01127             compgraph.getInternalGraph(),
01128             from,
01129             dfs_vis,
01130             CompColorMap(ccHashMap));
01131         DBGLOG(DBG,"predecessors of " << from << " are " << printrange(preds));
01132     }
01133 }
01134 
01135 
01136 bool GenuineGuessAndCheckModelGenerator::unfoundedSetCheck(InterpretationConstPtr partialInterpretation, InterpretationConstPtr assigned, InterpretationConstPtr changed, bool partial)
01137 {
01138 
01139     assert ( (!partial || (!!assigned && !!changed)) && "partial UFS checks require information about the assigned atoms");
01140 
01141     DBGLOG(DBG, "GenuineGuessAndCheckModelGenerator::unfoundedSetCheck");
01142     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "genuine g&c unfoundedSetCheck");
01143 
01144     DBGLOG(DBG, "unfoundedSetCheck was called to perform " << (partial ? "partial" : "full") << " UFS check");
01145 
01146     bool performCheck = false;
01147     static std::set<ID> emptySkipProgram;
01148 
01149     if (partial) {
01150         assert (!!assigned && !!changed);
01151 
01152         DBGLOG(DBG, "Calling UFS check heuristic");
01153 
01154         if (ufsCheckHeuristics->doUFSCheck(verifiedAuxes, partialInterpretation, assigned, changed)) {
01155 
01156             if (!factory.ctx.config.getOption("UFSCheck") && !factory.ctx.config.getOption("UFSCheckAssumptionBased")) {
01157                 LOG(WARNING, "Partial unfounded set checks are only possible if FLP check method is set to unfounded set check; will skip the check");
01158                 return true;
01159             }
01160 
01161             // ufs check without nogood learning makes no sense if the interpretation is not complete
01162             if (!factory.ctx.config.getOption("UFSLearning")) {
01163                 LOG(WARNING, "Partial unfounded set checks is useless if unfounded set learning is not enabled; will perform the check anyway, but result does not have any effect");
01164             }
01165 
01166             DBGLOG(DBG, "Heuristic decides to do a partial UFS check");
01167             performCheck = true;
01168         }
01169         else {
01170             DBGLOG(DBG, "Heuristic decides not to do an UFS check");
01171         }
01172     }
01173     else {
01174         DBGLOG(DBG, "Since the method was called for a full check, it will be performed");
01175         performCheck = true;
01176     }
01177 
01178     if (performCheck) {
01179         std::vector<IDAddress> ufs = ufscm->getUnfoundedSet(partialInterpretation,
01180             (partial ? ufsCheckHeuristics->getSkipProgram() : emptySkipProgram),
01181             factory.ctx.config.getOption("ExternalLearning") ? learnedEANogoods : SimpleNogoodContainerPtr());
01182         bool ufsFound = (ufs.size() > 0);
01183         #ifndef NDEBUG
01184         std::stringstream ss;
01185         ss << "UFS result: " << (ufsFound ? "" : "no ") << "UFS found (interpretation: " << *partialInterpretation;
01186         if (!!assigned) ss << ", assigned: " << *assigned;
01187         ss << ")";
01188         DBGLOG(DBG, ss.str());
01189         #endif
01190 
01191         if (ufsFound && factory.ctx.config.getOption("UFSLearning")) {
01192             Nogood ng = ufscm->getLastUFSNogood();
01193             DBGLOG(DBG, "Adding UFS nogood: " << ng);
01194 
01195             #ifndef NDEBUG
01196             // the learned nogood must not talk about unassigned atoms
01197             if (!!assigned) {
01198                 BOOST_FOREACH (ID lit, ng) {
01199                     assert(assigned->getFact(lit.address));
01200                 }
01201             }
01202             #endif
01203             solver->addNogood(ng);
01204             if (!!analysissolver) analysissolver->addNogood(ng);
01205         }
01206         return !ufsFound;
01207     }
01208     else {
01209         return true;
01210     }
01211 }
01212 
01213 
01214 IDAddress GenuineGuessAndCheckModelGenerator::getWatchedLiteral(int eaIndex, InterpretationConstPtr search, bool truthValue)
01215 {
01216 
01217     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "getWatchedLiteral");
01218     bm::bvector<>::enumerator eaDepAtoms = annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage().first();
01219     bm::bvector<>::enumerator eaDepAtoms_end = annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage().end();
01220     bm::bvector<>::enumerator searchb = search->getStorage().first();
01221     bm::bvector<>::enumerator searchb_end = search->getStorage().end();
01222 
01223     // go through eamask
01224     while (eaDepAtoms < eaDepAtoms_end) {
01225         // if search bitset has correct truth value
01226         if (search->getFact(*eaDepAtoms) == truthValue) {
01227             DBGLOG(DBG, "Found watch " << *eaDepAtoms << " for atom " << activeInnerEatoms[eaIndex]);
01228             return *eaDepAtoms;
01229         }
01230         eaDepAtoms++;
01231     }
01232 
01233     return ID::ALL_ONES;
01234 }
01235 
01236 
01237 void GenuineGuessAndCheckModelGenerator::unverifyExternalAtoms(InterpretationConstPtr changed)
01238 {
01239     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "genuine g&c unverifyEAtoms");
01240 
01241     DBGLOG(DBG, "Unverify External Atoms");
01242 
01243     // for all changed atoms
01244     bm::bvector<>::enumerator en = changed->getStorage().first();
01245     bm::bvector<>::enumerator en_end = changed->getStorage().end();
01246     while (en < en_end) {
01247         // for all external atoms which watch this atom for unverification
01248         BOOST_FOREACH (int eaIndex, unverifyWatchList[*en]) {
01249             if (eaEvaluated[eaIndex]) {
01250                 DBGLOG(DBG, "Unverifying external atom " << eaIndex);
01251 
01252                 // unverify
01253                 eaVerified[eaIndex] = false;
01254                 eaEvaluated[eaIndex] = false;
01255                 verifiedAuxes->getStorage() -= annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage();
01256 
01257                 // *en is our new watch (as it is either undefined or was recently changed)
01258                 verifyWatchList[*en].push_back(eaIndex);
01259             }
01260         }
01261         en++;
01262     }
01263     DBGLOG(DBG, "Unverify External Atoms finished");
01264 }
01265 
01266 
01267 bool GenuineGuessAndCheckModelGenerator::verifyExternalAtoms(InterpretationConstPtr partialInterpretation, InterpretationConstPtr assigned, InterpretationConstPtr changed)
01268 {
01269 
01270     // If there is no information about assigned or changed atoms, then we do not do anything.
01271     // This is because we would need to assume the worst (any atom might have changed and no atom is currently assigned).
01272     // Under these assumptions we cannot do any useful computation since we could only blindly evaluate any external atom,
01273     // but this can also be done later (when we have a concrete compatible set).
01274     if (!assigned || !changed) return false;
01275 
01276     DBGLOG(DBG, "Updating changed atoms sets");
01277     // update set of changed input atoms
01278     for (int eaIndex = 0; eaIndex < activeInnerEatoms.size(); ++eaIndex) {
01279         const ExternalAtom& eatom = reg->eatoms.getByID(activeInnerEatoms[eaIndex]);
01280         if (eatom.getExtSourceProperties().doesCareAboutChanged()) {
01281             assert (!!changedAtomsPerExternalAtom[eaIndex]);
01282             changedAtomsPerExternalAtom[eaIndex]->add(*changed);
01283         }
01284     }
01285 
01286     DBGLOG(DBG, "Verify External Atoms");
01287     // go through all changed atoms which are now assigned
01288     bool conflict = false;
01289     bm::bvector<>::enumerator en = changed->getStorage().first();
01290     bm::bvector<>::enumerator en_end = changed->getStorage().end();
01291     while (en < en_end) {
01292         if (assigned->getFact(*en)) {
01293 
01294             // first call high and then low frquency heuristics
01295             for (int hf = 1; hf >= 0; hf--) {
01296                 bool highFrequency = (hf == 1);
01297                 DBGLOG(DBG, "Calling " << (highFrequency ? "high" : "low") << " frequency heuristics");
01298 
01299                 // for all external atoms which watch this atom
01300                 // for low frquency heuristics we use unverifyWatchList by intend as it contains all atoms relevant to this external atom
01301                 std::vector<int>& watchlist = (highFrequency ? unverifyWatchList[*en] : verifyWatchList[*en]);
01302                 BOOST_FOREACH (int eaIndex, watchlist) {
01303                     assert (!!eaEvalHeuristics[eaIndex]);
01304 
01305                     if ((highFrequency == eaEvalHeuristics[eaIndex]->frequent()) && !eaEvaluated[eaIndex]) {
01306                         const ExternalAtom& eatom = reg->eatoms.getByID(activeInnerEatoms[eaIndex]);
01307 
01308                         // evaluate external atom if the heuristics decides so
01309                         DBGLOG(DBG, "Calling " << (highFrequency ? "high" : "low") << " frequency heuristics for external atom " << eaEvalHeuristics[eaIndex]);
01310                         if (eaEvalHeuristics[eaIndex]->doEvaluate(
01311                             eatom,
01312                             annotatedGroundProgram.getEAMask(eaIndex)->mask(),
01313                             annotatedGroundProgram.getProgramMask(),
01314                         partialInterpretation, assigned, changed)) {
01315                             // evaluate it
01316                             bool answeredFromCacheOrSupportSets;
01317                             DBGLOG(DBG, "Heuristic decides to evaluate external atom " << activeInnerEatoms[eaIndex]);
01318                             conflict |= verifyExternalAtom(eaIndex, partialInterpretation, assigned,
01319                                 eatom.getExtSourceProperties().doesCareAboutChanged() ? changedAtomsPerExternalAtom[eaIndex] : InterpretationConstPtr(),
01320                                 &answeredFromCacheOrSupportSets);
01321 
01322                             // if the external source was actually called, then clear the set of changed atoms (otherwise keep them until the source is actually called)
01323                             if (!answeredFromCacheOrSupportSets && eatom.getExtSourceProperties().doesCareAboutChanged()) {
01324                                 assert (!!changedAtomsPerExternalAtom[eaIndex]);
01325                                 DBGLOG(DBG, "Resetting changed atoms of external atom " << activeInnerEatoms[eaIndex]);
01326                                 changedAtomsPerExternalAtom[eaIndex]->clear();
01327                             }
01328                         }
01329 
01330                         // if the external atom is still not verified then find a new yet unassigned atom to watch
01331                         // (only necessary for low frequency heuristics since high frequency ones always watch all atoms)
01332                         if (!highFrequency && !eaEvaluated[eaIndex]) {
01333                             IDAddress id = getWatchedLiteral(eaIndex, assigned, false);
01334                             if (id != ID::ALL_ONES) verifyWatchList[id].push_back(eaIndex);
01335                         }
01336                     }
01337                 }
01338                 // current atom was set, so remove all watches
01339                 verifyWatchList[*en].clear();
01340             }
01341         }
01342 
01343         en++;
01344     }
01345 
01346     DBGLOG(DBG, "Verify External Atoms finished " << (conflict ? "with" : "without") << " conflict");
01347 
01348     return conflict;
01349 }
01350 
01351 
01352 bool GenuineGuessAndCheckModelGenerator::verifyExternalAtom(int eaIndex, InterpretationConstPtr partialInterpretation, InterpretationConstPtr assigned, InterpretationConstPtr changed, bool* answeredFromCacheOrSupportSets)
01353 {
01354 
01355     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "genuine g&c verifyEAtom");
01356 
01357     const ExternalAtom& eatom = reg->eatoms.getByID(activeInnerEatoms[eaIndex]);
01358 
01359     // if support sets are enabled, and the external atom provides complete support sets, we use them for verification
01360     if (!assigned && !changed && factory.ctx.config.getOption("SupportSets") &&
01361         (eatom.getExtSourceProperties().providesCompletePositiveSupportSets() || eatom.getExtSourceProperties().providesCompleteNegativeSupportSets()) &&
01362     annotatedGroundProgram.allowsForVerificationUsingCompleteSupportSets()) {
01363         if (answeredFromCacheOrSupportSets) *answeredFromCacheOrSupportSets = true;
01364         return verifyExternalAtomBySupportSets(eaIndex, partialInterpretation, assigned, changed);
01365     }
01366     else {
01367         return verifyExternalAtomByEvaluation(eaIndex, partialInterpretation, assigned, changed, answeredFromCacheOrSupportSets);
01368     }
01369 }
01370 
01371 
01372 bool GenuineGuessAndCheckModelGenerator::verifyExternalAtomByEvaluation(int eaIndex, InterpretationConstPtr partialInterpretation, InterpretationConstPtr assigned, InterpretationConstPtr changed, bool* answeredFromCache)
01373 {
01374     assert (!!partialInterpretation && "interpretation not set");
01375 
01376     if (factory.ctx.config.getOption("ExternalAtomVerificationFromLearnedNogoods")) {
01377         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sideav, "gen. g&c verifyEAtom by eav (attempt)");
01378         InterpretationConstPtr verifiedAuxes = eavTree.getVerifiedAuxiliaries(partialInterpretation, assigned, factory.ctx.registry());
01379 
01380         // check if all auxes are verified
01381         bm::bvector<>::enumerator en = annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage().first();
01382         bm::bvector<>::enumerator en_end = annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage().end();
01383         while (en < en_end) {
01384             if (factory.ctx.registry()->ogatoms.getIDByAddress(*en).isExternalAuxiliary()) {
01385                 if (!verifiedAuxes->getFact(*en)) break;
01386             }
01387             en++;
01388         }
01389         if (en == en_end) {
01390             // verified
01391             DBGLOG(DBG, "Verified external atom without evaluation");
01392             DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sideavs, "gen. g&c verifyEAtom by eav (succeed)", 1);
01393             eaEvaluated[eaIndex] = true;
01394             eaVerified[eaIndex] = true;
01395             return true;
01396         }else{
01397             DBGLOG(DBG, "Could not verify external atom without evaluation --> will evaluate");
01398         }
01399     }
01400 
01401     {
01402         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "gen. g&c verifyEAtom by eval.");
01403 
01404         // prepare EA evaluation
01405         InterpretationConstPtr mask = (annotatedGroundProgram.getEAMask(eaIndex)->mask());
01406         DBGLOG(DBG, "Initializing VerifyExternalAtomCB");
01407         VerifyExternalAtomCB vcb(partialInterpretation, factory.ctx.registry()->eatoms.getByID(activeInnerEatoms[eaIndex]), *(annotatedGroundProgram.getEAMask(eaIndex)));
01408 
01409         DBGLOG(DBG, "Assigning all auxiliary inputs");
01410         InterpretationConstPtr evalIntr = partialInterpretation;
01411         if (!factory.ctx.config.getOption("IncludeAuxInputInAuxiliaries")) {
01412             // make sure that ALL input auxiliary atoms are true, otherwise we might miss some output atoms and consider true output atoms wrongly as unfounded
01413             // clone and extend
01414             InterpretationPtr ncevalIntr(new Interpretation(*partialInterpretation));
01415             ncevalIntr->getStorage() |= annotatedGroundProgram.getEAMask(eaIndex)->getAuxInputMask()->getStorage();
01416             evalIntr = ncevalIntr;
01417         }
01418 
01419         // evaluate the external atom and learn nogoods if external learning is used
01420         if (!!assigned) {
01421              DBGLOG(DBG, "Verifying external Atom " << activeInnerEatoms[eaIndex] << " under " << *evalIntr << " (assigned: " << *assigned << ")");
01422         }else{
01423             DBGLOG(DBG, "Verifying external Atom " << activeInnerEatoms[eaIndex] << " under " << *evalIntr << " (assigned: all)");
01424         }
01425         evaluateExternalAtom(factory.ctx, activeInnerEatoms[eaIndex], evalIntr, vcb,
01426             factory.ctx.config.getOption("ExternalLearning") ? learnedEANogoods : NogoodContainerPtr(), assigned, changed, answeredFromCache);
01427         updateEANogoods(partialInterpretation, assigned, changed);
01428 
01429         // if the input to the external atom was complete, then remember the verification result;
01430         // for incomplete input we cannot yet decide this yet, evaluation is only done for learning purposes in this case
01431         DBGLOG(DBG, "Checking whether verification result is to be stored");
01432         if( !assigned ||
01433             (annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage() & annotatedGroundProgram.getProgramMask()->getStorage()).count() == (assigned->getStorage() & annotatedGroundProgram.getProgramMask()->getStorage()).count()) {
01434             eaVerified[eaIndex] = vcb.verify();
01435 
01436             DBGLOG(DBG, "Verifying " << activeInnerEatoms[eaIndex] << " (Result: " << eaVerified[eaIndex] << ")");
01437 
01438             // we remember that we evaluated, only if there is a propagator that can undo this memory (that can unverify an eatom during model search)
01439             if(factory.ctx.config.getOption("NoPropagator") == 0) {
01440                 DBGLOG(DBG, "Setting external atom status of " << eaIndex << " to evaluated");
01441                 eaEvaluated[eaIndex] = true;
01442                if (eaVerified[eaIndex]) verifiedAuxes->getStorage() |= annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage();
01443             }
01444 
01445             return !eaVerified[eaIndex];
01446         }
01447         else {
01448             return false;
01449         }
01450     }
01451 }
01452 
01453 
01454 bool GenuineGuessAndCheckModelGenerator::verifyExternalAtomBySupportSets(int eaIndex, InterpretationConstPtr partialInterpretation, InterpretationConstPtr assigned, InterpretationConstPtr changed)
01455 {
01456 
01457     assert (!assigned && !changed && " verification using complete support sets is only possible wrt. complete interpretations");
01458     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid, "genuine g&c verifyEAtom by suoport sets");
01459 
01460     eaVerified[eaIndex] = annotatedGroundProgram.verifyExternalAtomsUsingCompleteSupportSets(eaIndex, partialInterpretation, InterpretationPtr());
01461     if (eaVerified[eaIndex]) verifiedAuxes->getStorage() |= annotatedGroundProgram.getEAMask(eaIndex)->mask()->getStorage();
01462 
01463     // we remember that we evaluated, only if there is a propagator that can undo this memory (that can unverify an eatom during model search)
01464     if( factory.ctx.config.getOption("NoPropagator") == 0 ) {
01465         DBGLOG(DBG, "Setting external atom status of " << eaIndex << " to evaluated");
01466         eaEvaluated[eaIndex] = true;
01467     }
01468 
01469     return !eaVerified[eaIndex];
01470 }
01471 
01472 
01473 const OrdinaryASPProgram& GenuineGuessAndCheckModelGenerator::getGroundProgram()
01474 {
01475     return annotatedGroundProgram.getGroundProgram();
01476 }
01477 
01478 
01479 void GenuineGuessAndCheckModelGenerator::propagate(InterpretationConstPtr partialAssignment, InterpretationConstPtr assigned, InterpretationConstPtr changed)
01480 {
01481 
01482     assert (!!partialAssignment && !!assigned && !!changed);
01483 
01484     // update external atom verification results
01485     // (1) unverify external atoms if atoms, which are relevant to this external atom, have (potentially) changed
01486     unverifyExternalAtoms(changed);
01487     // (2) now verify external atoms (driven by a heuristic)
01488     bool conflict = verifyExternalAtoms(partialAssignment, assigned, changed);
01489 
01490     // UFS check can in principle also applied to conflicting assignments
01491     // since the heuristic knows which external atoms are correct and which ones not.
01492     // The check can be restricted to the non-conflicting part of the program.
01493     // Although there is already another reason for backtracking,
01494     // we still need to notify the heuristics such that it can update its internal information about assigned atoms.
01495     assert (!!ufsCheckHeuristics);
01496     ufsCheckHeuristics->updateSkipProgram(verifiedAuxes, partialAssignment, assigned, changed);
01497     if (!conflict) {
01498         if (annotatedGroundProgram.hasHeadCycles() == 0 && annotatedGroundProgram.hasECycles() == 0 &&
01499         factory.ctx.config.getOption("FLPDecisionCriterionHead") && factory.ctx.config.getOption("FLPDecisionCriterionE")) {
01500             DBGLOG(DBG, "No head- or e-cycles --> No FLP/UFS check necessary");
01501         }else{
01502             unfoundedSetCheck(partialAssignment, assigned, changed, true);
01503         }
01504     }else{
01505         ufsCheckHeuristics->notify(verifiedAuxes, partialAssignment, assigned, changed);
01506     }
01507 }
01508 
01509 
01510 DLVHEX_NAMESPACE_END
01511 
01512 
01513 // vim:expandtab:ts=4:sw=4:
01514 // mode: C++
01515 // End:
01516