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