dlvhex
2.5.0
|
00001 /* dlvhex -- Answer-Set Programming with external interfaces. 00002 * Copyright (C) 2005-2007 Roman Schindlauer 00003 * Copyright (C) 2006-2015 Thomas Krennwallner 00004 * Copyright (C) 2009-2016 Peter Schüller 00005 * Copyright (C) 2011-2016 Christoph Redl 00006 * Copyright (C) 2015-2016 Tobias Kaminski 00007 * Copyright (C) 2015-2016 Antonius Weinzierl 00008 * 00009 * This file is part of dlvhex. 00010 * 00011 * dlvhex is free software; you can redistribute it and/or modify it 00012 * under the terms of the GNU Lesser General Public License as 00013 * published by the Free Software Foundation; either version 2.1 of 00014 * the License, or (at your option) any later version. 00015 * 00016 * dlvhex is distributed in the hope that it will be useful, but 00017 * WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with dlvhex; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00024 * 02110-1301 USA. 00025 */ 00026 00034 #ifdef HAVE_CONFIG_H 00035 #include "config.h" 00036 #endif // HAVE_CONFIG_H 00037 00038 #include "dlvhex2/BaseModelGenerator.h" 00039 #include "dlvhex2/Logger.h" 00040 #include "dlvhex2/Registry.h" 00041 #include "dlvhex2/Printer.h" 00042 #include "dlvhex2/ASPSolver.h" 00043 #include "dlvhex2/ProgramCtx.h" 00044 #include "dlvhex2/PluginInterface.h" 00045 #include "dlvhex2/Benchmarking.h" 00046 #include "dlvhex2/Atoms.h" 00047 #include "dlvhex2/ExternalLearningHelper.h" 00048 #include "dlvhex2/LiberalSafetyChecker.h" 00049 00050 #include <boost/foreach.hpp> 00051 #include <boost/ptr_container/ptr_vector.hpp> 00052 #include <boost/unordered_set.hpp> 00053 00054 #include <fstream> 00055 00056 DLVHEX_NAMESPACE_BEGIN 00057 00058 // we define this class here, as we use it only in the implementation 00059 // it is stored in registry as an opaque shared pointer 00060 class EAInputTupleCache 00061 { 00062 protected: 00063 // a vector of pointers to tuple, with null values allowed 00064 // we use this similar to a bitset: 00065 // 00066 // the idaddress of an ordinary ground atom 00067 // with predicate externalatom::auxinputpredicate 00068 // is the address in this vector 00069 // 00070 // (null values are for all ordinary ground atoms which are not auxiliary input predicates) 00071 // we trade this null space for addressing speed, as these tuples need to be looked up very often 00072 // 00073 // the stored tuples are input tuples to external atoms, with all replacements of variables 00074 // due to externalatom::auxinputmapping already done 00075 boost::ptr_vector< boost::nullable< Tuple > > cache; 00076 00077 public: 00078 // nothing virtual, this is for implementation 00079 // and virtual function calls could slow us down 00080 EAInputTupleCache(): cache() {} 00081 // free the cache and delete all non-NULL pointers (automatically) 00082 ~EAInputTupleCache() {} 00083 00084 // just looks up and asserts everything is ok 00085 inline const Tuple& lookup(IDAddress auxInputOgAtomAddress) const 00086 { 00087 assert(auxInputOgAtomAddress < cache.size()); 00088 assert( !cache.is_null(auxInputOgAtomAddress) ); 00089 return cache[auxInputOgAtomAddress]; 00090 } 00091 00092 // looks up tuple in vector and returns it 00093 // creates empty tuple in vector and returns it if nothing was stored in vector 00094 // 00095 // resizes vector if necessary 00096 inline Tuple& lookupOrCreate(IDAddress auxInputOgAtomAddress) { 00097 if( auxInputOgAtomAddress >= cache.size() ) 00098 cache.resize(auxInputOgAtomAddress+1, NULL); 00099 if( cache.is_null(auxInputOgAtomAddress) ) { 00100 cache.replace(auxInputOgAtomAddress, new Tuple); 00101 } 00102 return cache[auxInputOgAtomAddress]; 00103 } 00104 }; 00105 typedef boost::shared_ptr<EAInputTupleCache> EAInputTupleCachePtr; 00106 00107 BaseModelGenerator::ExternalAnswerTupleCallback:: 00108 ~ExternalAnswerTupleCallback() 00109 { 00110 } 00111 00112 00113 BaseModelGenerator::ExternalAnswerTupleMultiCallback::~ExternalAnswerTupleMultiCallback() 00114 { 00115 } 00116 00117 00118 bool BaseModelGenerator::ExternalAnswerTupleMultiCallback::eatom(const ExternalAtom& eatom) 00119 { 00120 bool cont = true; 00121 BOOST_FOREACH (ExternalAnswerTupleCallback* cb, callbacks) cont &= cb->eatom(eatom); 00122 return cont; 00123 } 00124 00125 00126 bool BaseModelGenerator::ExternalAnswerTupleMultiCallback::input(const Tuple& input) 00127 { 00128 bool cont = true; 00129 BOOST_FOREACH (ExternalAnswerTupleCallback* cb, callbacks) cont &= cb->input(input); 00130 return cont; 00131 } 00132 00133 00134 bool BaseModelGenerator::ExternalAnswerTupleMultiCallback::output(const Tuple& output) 00135 { 00136 bool cont = true; 00137 BOOST_FOREACH (ExternalAnswerTupleCallback* cb, callbacks) cont &= cb->output(output); 00138 return cont; 00139 } 00140 00141 00142 BaseModelGenerator:: 00143 IntegrateExternalAnswerIntoInterpretationCB:: 00144 IntegrateExternalAnswerIntoInterpretationCB( 00145 InterpretationPtr outputi): 00146 outputi(outputi), 00147 reg(outputi->getRegistry()), 00148 replacement(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_EXTERNALAUX) 00149 { 00150 } 00151 00152 00153 bool 00154 BaseModelGenerator::IntegrateExternalAnswerIntoInterpretationCB:: 00155 eatom(const ExternalAtom& eatom) 00156 { 00157 replacement.tuple.resize(1); 00158 replacement.tuple[0] = 00159 reg->getAuxiliaryConstantSymbol('r', eatom.predicate); 00160 00161 // never abort 00162 return true; 00163 } 00164 00165 00166 // remembers input 00167 bool 00168 BaseModelGenerator::IntegrateExternalAnswerIntoInterpretationCB:: 00169 input(const Tuple& input) 00170 { 00171 assert(replacement.tuple.size() >= 1); 00172 // shorten 00173 replacement.tuple.resize(1); 00174 // add 00175 replacement.tuple.insert(replacement.tuple.end(), 00176 input.begin(), input.end()); 00177 00178 // never abort 00179 return true; 00180 } 00181 00182 00183 // creates replacement ogatom and activates respective bit in output interpretation 00184 bool 00185 BaseModelGenerator::IntegrateExternalAnswerIntoInterpretationCB:: 00186 output(const Tuple& output) 00187 { 00188 assert(replacement.tuple.size() >= 1); 00189 // add, but remember size to reset it later 00190 unsigned size = replacement.tuple.size(); 00191 replacement.tuple.insert(replacement.tuple.end(), 00192 output.begin(), output.end()); 00193 00194 // this replacement might already exists 00195 DBGLOG(DBG,"integrating eatom tuple " << printrange(replacement.tuple)); 00196 ID idreplacement = reg->storeOrdinaryGAtom(replacement); 00197 DBGLOG(DBG,"got replacement ID " << idreplacement); 00198 outputi->setFact(idreplacement.address); 00199 DBGLOG(DBG,"output interpretation is now " << *outputi); 00200 00201 // shorten it, s.t. we can add the next one 00202 replacement.tuple.resize(size); 00203 00204 // never abort 00205 return true; 00206 } 00207 00208 00209 BaseModelGenerator::VerifyExternalAnswerAgainstPosNegGuessInterpretationCB:: 00210 VerifyExternalAnswerAgainstPosNegGuessInterpretationCB( 00211 InterpretationPtr _guess_pos, 00212 InterpretationPtr _guess_neg): 00213 reg(_guess_pos->getRegistry()), 00214 guess_pos(_guess_pos), 00215 guess_neg(_guess_neg), 00216 replacement(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX) 00217 { 00218 assert(guess_pos->getRegistry() == guess_neg->getRegistry()); 00219 } 00220 00221 00222 bool 00223 BaseModelGenerator::VerifyExternalAnswerAgainstPosNegGuessInterpretationCB:: 00224 eatom(const ExternalAtom& eatom) 00225 { 00226 pospred = 00227 reg->getAuxiliaryConstantSymbol('r', eatom.predicate); 00228 negpred = 00229 reg->getAuxiliaryConstantSymbol('n', eatom.predicate); 00230 replacement.tuple.resize(1); 00231 00232 // never abort 00233 return true; 00234 } 00235 00236 00237 bool 00238 BaseModelGenerator::VerifyExternalAnswerAgainstPosNegGuessInterpretationCB:: 00239 input(const Tuple& input) 00240 { 00241 assert(replacement.tuple.size() >= 1); 00242 00243 // shorten 00244 replacement.tuple.resize(1); 00245 00246 // add 00247 replacement.tuple.insert(replacement.tuple.end(), 00248 input.begin(), input.end()); 00249 00250 // never abort 00251 return true; 00252 } 00253 00254 00255 bool 00256 BaseModelGenerator::VerifyExternalAnswerAgainstPosNegGuessInterpretationCB:: 00257 output(const Tuple& output) 00258 { 00259 assert(replacement.tuple.size() >= 1); 00260 00261 // add, but remember size to reset it later 00262 unsigned size = replacement.tuple.size(); 00263 replacement.tuple.insert(replacement.tuple.end(), 00264 output.begin(), output.end()); 00265 00266 // build pos replacement, register, and clear the corresponding bit in guess_pos 00267 replacement.tuple[0] = pospred; 00268 ID idreplacement_pos = reg->storeOrdinaryGAtom(replacement); 00269 DBGLOG(DBG,"pos replacement ID = " << idreplacement_pos); 00270 if( !guess_pos->getFact(idreplacement_pos.address) ) { 00271 // check whether neg is true, if yes we bailout 00272 replacement.tuple[0] = negpred; 00273 ID idreplacement_neg = reg->ogatoms.getIDByTuple(replacement.tuple); 00274 if( idreplacement_neg == ID_FAIL ) { 00275 // this is ok, the negative replacement does not exist so it cannot be true 00276 DBGLOG(DBG,"neg eatom replacement " << replacement << " not found -> not required"); 00277 } 00278 else { 00279 DBGLOG(DBG,"neg eatom replacement ID = " << idreplacement_neg); 00280 00281 // verify if it is true or not 00282 if( guess_neg->getFact(idreplacement_neg.address) == true ) { 00283 // this is bad, the guess was "false" but the eatom output says it is "true" 00284 // -> abort 00285 DBGLOG(DBG,"neg eatom replacement is true in guess -> wrong guess!"); 00286 00287 // (we now that we won't reuse replacement.tuple, 00288 // so we do not care about resizing it here) 00289 return false; 00290 } 00291 else { 00292 // this is ok, the negative replacement exists but is not true 00293 DBGLOG(DBG,"neg eatom replacement found but not set -> ok"); 00294 } 00295 } 00296 } 00297 else { 00298 // remove this bit, so later we can check if all bits were cleared 00299 // (i.e., if all positive guesses were confirmed) 00300 guess_pos->clearFact(idreplacement_pos.address); 00301 DBGLOG(DBG,"clearing replacement fact -> positive guess interpretation is now " << *guess_pos); 00302 } 00303 00304 // shorten it, s.t. we can add the next one 00305 replacement.tuple.resize(size); 00306 00307 // do not abort if we reach here 00308 return true; 00309 } 00310 00311 00312 BaseModelGenerator::VerifyExternalAtomCB::VerifyExternalAtomCB(InterpretationConstPtr guess, const ExternalAtom& eatom, const ExternalAtomMask& eaMask) : guess(guess), remainingguess(), verified(true), exatom(eatom), eaMask(eaMask), replacement(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX | ID::PROPERTY_EXTERNALAUX), falsified(ID_FAIL) 00313 { 00314 00315 reg = eatom.pluginAtom->getRegistry(); 00316 00317 pospred = reg->getAuxiliaryConstantSymbol('r', exatom.predicate); 00318 negpred = reg->getAuxiliaryConstantSymbol('n', exatom.predicate); 00319 replacement.tuple.resize(1); 00320 00321 remainingguess = InterpretationPtr(new Interpretation(reg)); 00322 remainingguess->add(*guess); 00323 remainingguess->getStorage() &= eaMask.mask()->getStorage(); 00324 } 00325 00326 00327 BaseModelGenerator::VerifyExternalAtomCB::~VerifyExternalAtomCB() 00328 { 00329 } 00330 00331 00332 bool BaseModelGenerator::VerifyExternalAtomCB::onlyNegativeAuxiliaries() 00333 { 00334 00335 bm::bvector<>::enumerator en = remainingguess->getStorage().first(); 00336 bm::bvector<>::enumerator en_end = remainingguess->getStorage().end(); 00337 00338 while (en < en_end) { 00339 const OrdinaryAtom& oatom = reg->ogatoms.getByAddress(*en); 00340 if (oatom.tuple[0] == pospred) { 00341 DBGLOG(DBG, "Unfounded positive auxiliary detected: " << printToString<RawPrinter>(reg->ogatoms.getIDByAddress(*en), reg)); 00342 falsified = reg->ogatoms.getIDByAddress(*en); 00343 return false; 00344 } 00345 en++; 00346 } 00347 return true; 00348 } 00349 00350 00351 bool BaseModelGenerator::VerifyExternalAtomCB::eatom(const ExternalAtom& exatom) 00352 { 00353 00354 // this callback must not be used for evaluating multiple external atoms 00355 assert(&exatom == &this->exatom); 00356 00357 return true; 00358 } 00359 00360 00361 bool BaseModelGenerator::VerifyExternalAtomCB::input(const Tuple& input) 00362 { 00363 00364 assert(replacement.tuple.size() >= 1); 00365 00366 // shorten 00367 replacement.tuple.resize(1); 00368 00369 // add 00370 replacement.tuple.insert(replacement.tuple.end(), input.begin(), input.end()); 00371 00372 // never abort 00373 return true; 00374 } 00375 00376 00377 bool BaseModelGenerator::VerifyExternalAtomCB::output(const Tuple& output) 00378 { 00379 00380 assert(replacement.tuple.size() >= 1); 00381 00382 // add, but remember size to reset it later 00383 unsigned size = replacement.tuple.size(); 00384 replacement.tuple.insert(replacement.tuple.end(), output.begin(), output.end()); 00385 00386 // build pos replacement, register, and clear the corresponding bit in guess_pos 00387 replacement.tuple[0] = pospred; 00388 ID idreplacement_pos = reg->storeOrdinaryGAtom(replacement); 00389 replacement.tuple[0] = negpred; 00390 ID idreplacement_neg = reg->storeOrdinaryGAtom(replacement); 00391 00392 // shorten it, s.t. we can add the next one 00393 replacement.tuple.resize(size); 00394 00395 if(remainingguess->getFact(idreplacement_neg.address)) { 00396 LOG(DBG, "Positive atom " << printToString<RawPrinter>(idreplacement_pos, reg) << " address=" << idreplacement_pos.address << " was guessed to be false!"); 00397 verified = false; 00398 falsified = reg->ogatoms.getIDByAddress(idreplacement_neg.address); 00399 return false; 00400 } 00401 else { 00402 DBGLOG(DBG, "Positive atom was guessed correctly"); 00403 remainingguess->clearFact(idreplacement_pos.address); 00404 return true; 00405 } 00406 } 00407 00408 00409 bool BaseModelGenerator::VerifyExternalAtomCB::verify() 00410 { 00411 00412 if (remainingguess) { 00413 if (!onlyNegativeAuxiliaries()) { 00414 verified = false; 00415 } 00416 remainingguess.reset(); 00417 } 00418 00419 return verified; 00420 } 00421 00422 00423 ID BaseModelGenerator::VerifyExternalAtomCB::getFalsifiedAtom() 00424 { 00425 return falsified; 00426 } 00427 00428 00429 // projects input interpretation 00430 // calls eatom function 00431 // reintegrates output tuples as auxiliary atoms into outputi 00432 // (inputi and outputi may point to the same interpretation) 00433 00434 bool BaseModelGenerator::evaluateExternalAtom(ProgramCtx& ctx, 00435 ID eatomID, 00436 InterpretationConstPtr inputi, 00437 ExternalAnswerTupleCallback& cb, 00438 NogoodContainerPtr nogoods, 00439 InterpretationConstPtr assigned, 00440 InterpretationConstPtr changed, 00441 bool* fromCache) const 00442 { 00443 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sideea,"evaluate external atom"); 00444 if (!!assigned){ 00445 DLVHEX_BENCHMARK_REGISTER_AND_COUNT(sideeapart,"evaluate external atom part.", 1); 00446 } 00447 LOG_SCOPE(PLUGIN,"eEA",false); 00448 RegistryPtr reg = ctx.registry(); 00449 DBGLOG(DBG,"eEA = evaluateExternalAtom for " << printToString<RawPrinter>(eatomID, reg) << "/" << eatomID); 00450 00451 const ExternalAtom& eatom = reg->eatoms.getByID(eatomID); 00452 00453 // build input interpretation 00454 // for each input tuple (multiple auxiliary inputs possible) 00455 // build query 00456 // call retrieve 00457 // integrate answer into interpretation i as additional facts 00458 00459 // if this is wrong, we might have mixed up registries between plugin and program 00460 assert(!!eatom.pluginAtom && eatom.predicate == eatom.pluginAtom->getPredicateID()); 00461 00462 // update masks (inputMask and auxInputMask) 00463 eatom.updatePredicateInputMask(); 00464 00465 #ifndef NDEBUG 00466 if (!!assigned) { 00467 DBGLOG(DBG, "Assigned input atoms: " << (assigned->getStorage() & eatom.getPredicateInputMask()->getStorage()).count() << " out of " << eatom.getPredicateInputMask()->getStorage().count() << " (total number of assigned atoms" << assigned->getStorage().count() << ")"); 00468 }else{ 00469 DBGLOG(DBG, "Assigned input atoms: all"); 00470 } 00471 #endif 00472 00473 // project interpretation for predicate inputs 00474 InterpretationConstPtr eatominp = 00475 projectEAtomInputInterpretation(ctx.registry(), eatom, inputi); 00476 DBGLOG(DBG,"projected input interpretation = " << *eatominp); 00477 00478 InterpretationConstPtr eatomassigned; 00479 if (assigned) eatomassigned = projectEAtomInputInterpretation(ctx.registry(), eatom, assigned); 00480 00481 InterpretationConstPtr eatomchanged; 00482 if (changed) eatomchanged = projectEAtomInputInterpretation(ctx.registry(), eatom, changed); 00483 00484 InterpretationPtr pim = InterpretationPtr(new Interpretation(ctx.registry())); 00485 pim->add(*eatom.getPredicateInputMask()); 00486 if( eatom.auxInputPredicate == ID_FAIL ) { 00487 // only one input tuple, and that is the one stored in eatom.inputs 00488 00489 // prepare callback for evaluation of this eatom 00490 if( !cb.eatom(eatom) ) { 00491 LOG(DBG,"callback aborted for eatom " << printToString<RawPrinter>(eatomID, reg)); 00492 return false; 00493 } 00494 00495 // XXX here we copy it, we should just reference it 00496 PluginAtom::Query query(&ctx, eatominp, eatom.inputs, eatom.tuple, eatomID, pim /*InterpretationPtr()*/, eatomassigned, eatomchanged); 00497 // XXX make this part of constructor 00498 return evaluateExternalAtomQuery(query, cb, nogoods, fromCache); 00499 } 00500 else { 00501 // auxiliary input predicate -> get input tuples (with cache) 00502 00503 // ensure we have a cache for external atom input tuples 00504 if( !reg->eaInputTupleCache ) 00505 reg->eaInputTupleCache.reset(new EAInputTupleCache); 00506 EAInputTupleCache& eaitc = *reg->eaInputTupleCache; 00507 00508 // build input tuples 00509 // (we associate input tuples in the cache with the auxiliary external 00510 // atom input tuples they have been created from) 00511 // (for eatoms where no auxiliary input is required, we directly use ExternalAtom::inputs) 00512 InterpretationPtr inputs(new Interpretation(reg)); 00513 // allocates inputs if necessary 00514 buildEAtomInputTuples(ctx.registry(), eatom, inputi, inputs); 00515 00516 Interpretation::TrueBitIterator bit, bit_end; 00517 boost::tie(bit, bit_end) = inputs->trueBits(); 00518 00519 if( bit != bit_end ) { 00520 // we have an input atom, so we tell the callback that we will process it 00521 if( !cb.eatom(eatom) ) { 00522 LOG(DBG,"callback aborted for eatom " << printToString<RawPrinter>(eatomID, reg)); 00523 return false; 00524 } 00525 00526 for(;bit != bit_end; ++bit) { 00527 const Tuple& inputtuple = eaitc.lookup(*bit); 00528 // build query as reference to the storage in cache 00529 // XXX here we copy, we could make it const ref in Query 00530 PluginAtom::Query query(&ctx, eatominp, inputtuple, eatom.tuple, eatomID, pim /*InterpretationPtr()*/, eatomassigned, eatomchanged); 00531 if( ! evaluateExternalAtomQuery(query, cb, nogoods, fromCache) ) 00532 return false; 00533 } 00534 } 00535 } 00536 return true; 00537 } 00538 00539 00540 namespace 00541 { 00542 void warnTupleMismatch(const ExternalAtom& eatom, const Tuple& t) { 00543 static boost::unordered_set<void*> warned; 00544 void *p = reinterpret_cast<void*>(eatom.pluginAtom); 00545 if( warned.count(p) == 0 ) { 00546 warned.insert(p); 00547 LOG(WARNING,"external atom " << eatom << " returned tuple " << 00548 printrange(t) << " which does not match output pattern (skipping, suppressing future warnings)"); 00549 } 00550 } 00551 } 00552 00553 00554 bool BaseModelGenerator::evaluateExternalAtomQuery( 00555 PluginAtom::Query& query, 00556 ExternalAnswerTupleCallback& cb, 00557 NogoodContainerPtr nogoods, 00558 bool* fromCache) const 00559 { 00560 const ProgramCtx& ctx = *query.ctx; 00561 const RegistryPtr reg = ctx.registry(); 00562 const ExternalAtom& eatom = ctx.registry()->eatoms.getByID(query.eatomID); 00563 const Tuple& inputtuple = query.input; 00564 00565 if( Logger::Instance().shallPrint(Logger::PLUGIN) ) { 00566 LOG(PLUGIN,"eatom projected interpretation = " << *query.interpretation); 00567 LOG(PLUGIN,"eatom input pattern = " << printManyToString<RawPrinter>(eatom.inputs, ",", reg)); 00568 LOG(PLUGIN,"eatom output pattern = " << printManyToString<RawPrinter>(eatom.tuple, ",", reg)); 00569 LOG(PLUGIN,"eatom input tuple = " << printManyToString<RawPrinter>(inputtuple, ",", reg)); 00570 } 00571 00572 PluginAtom::Answer answer; 00573 assert(!!eatom.pluginAtom); 00574 bool fromCache_ = eatom.pluginAtom->retrieveFacade(query, answer, nogoods, query.ctx->config.getOption("UseExtAtomCache")); 00575 if (fromCache) *fromCache = fromCache_; 00576 LOG(PLUGIN,"got " << answer.get().size() << " answer tuples"); 00577 00578 if( !answer.get().empty() ) { 00579 Tuple it; 00580 if (ctx.config.getOption("IncludeAuxInputInAuxiliaries") && eatom.auxInputPredicate != ID_FAIL) { 00581 it.push_back(eatom.auxInputPredicate); 00582 } 00583 BOOST_FOREACH (ID i, inputtuple) it.push_back(i); 00584 if( !cb.input(it) ) { 00585 LOG(DBG,"callback aborted for input tuple " << printrange(inputtuple)); 00586 return false; 00587 } 00588 } 00589 00590 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidier,"integrate external results"); 00591 00592 // integrate result into interpretation 00593 BOOST_FOREACH(const Tuple& t, answer.get()) { 00594 LOG(PLUGIN,"got answer tuple " << printManyToString<RawPrinter>(t, ",", reg)); 00595 if( !verifyEAtomAnswerTuple(reg, eatom, t) ) { 00596 warnTupleMismatch(eatom, t); 00597 continue; 00598 } 00599 00600 // call callback and abort if requested 00601 if( !cb.output(t) ) { 00602 LOG(DBG,"callback aborted for output tuple <" << printManyToString<RawPrinter>(t, ",", reg) << ">"); 00603 return false; 00604 } 00605 } 00606 00607 return true; 00608 } 00609 00610 00611 void BaseModelGenerator::learnSupportSetsForExternalAtom(ProgramCtx& ctx, 00612 ID eatomID, 00613 NogoodContainerPtr nogoods) const 00614 { 00615 00616 LOG_SCOPE(PLUGIN,"lSS",false); 00617 DBGLOG(DBG,"= learnSupportSetsForExternalAtom for " << eatomID); 00618 00619 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidlss,"learn support sets for external atom"); 00620 const ExternalAtom& eatom = ctx.registry()->eatoms.getByID(eatomID); 00621 00622 RegistryPtr reg = ctx.registry(); 00623 00624 // build input interpretation 00625 // for each input tuple (multiple auxiliary inputs possible) 00626 // build query 00627 // call learn support sets 00628 00629 // if this is wrong, we might have mixed up registries between plugin and program 00630 assert(!!eatom.pluginAtom && eatom.getExtSourceProperties().providesSupportSets() && eatom.predicate == eatom.pluginAtom->getPredicateID()); 00631 00632 // update masks (inputMask and auxInputMask) 00633 eatom.updatePredicateInputMask(); 00634 00635 // prepare maximum interpretation 00636 InterpretationPtr eatominp = InterpretationPtr(new Interpretation(reg)); 00637 eatominp->add(*eatom.getPredicateInputMask()); 00638 00639 InterpretationPtr pim = InterpretationPtr(new Interpretation(ctx.registry())); 00640 pim->add(*eatom.getPredicateInputMask()); 00641 if( eatom.auxInputPredicate == ID_FAIL ) { 00642 // only one input tuple, and that is the one stored in eatom.inputs 00643 00644 // prepare query 00645 // XXX here we copy it, we should just reference it 00646 PluginAtom::Query query(&ctx, eatom.getPredicateInputMask(), eatom.inputs, eatom.tuple, eatomID, pim); 00647 // XXX make this part of constructor 00648 eatom.pluginAtom->learnSupportSets(query, nogoods); 00649 } 00650 else { 00651 eatominp->add(*eatom.getAuxInputMask()); 00652 00653 // auxiliary input predicate -> get input tuples (with cache) 00654 00655 // ensure we have a cache for external atom input tuples 00656 if( !reg->eaInputTupleCache ) 00657 reg->eaInputTupleCache.reset(new EAInputTupleCache); 00658 EAInputTupleCache& eaitc = *reg->eaInputTupleCache; 00659 00660 // for all input tuples 00661 Interpretation::TrueBitIterator bit, bit_end; 00662 boost::tie(bit, bit_end) = eatom.getAuxInputMask()->trueBits(); 00663 00664 if( bit != bit_end ) { 00665 00666 for(;bit != bit_end; ++bit) { 00667 const Tuple& inputtuple = eaitc.lookup(*bit); 00668 // build query as reference to the storage in cache 00669 // XXX here we copy, we could make it const ref in Query 00670 PluginAtom::Query query(&ctx, eatom.getPredicateInputMask(), inputtuple, eatom.tuple, eatomID); 00671 eatom.pluginAtom->learnSupportSets(query, nogoods); 00672 } 00673 } 00674 } 00675 } 00676 00677 00678 // calls evaluateExternalAtom for each atom in eatoms 00679 00680 bool BaseModelGenerator::evaluateExternalAtoms(ProgramCtx& ctx, 00681 const std::vector<ID>& eatoms, 00682 InterpretationConstPtr inputi, 00683 ExternalAnswerTupleCallback& cb, 00684 NogoodContainerPtr nogoods) const 00685 { 00686 BOOST_FOREACH(ID eatomid, eatoms) { 00687 if( !evaluateExternalAtom(ctx, eatomid, inputi, cb, nogoods) ) { 00688 LOG(DBG,"callbacks aborted evaluateExternalAtoms"); 00689 return false; 00690 } 00691 } 00692 return true; 00693 } 00694 00695 00696 // returns false iff tuple does not unify with eatom output pattern 00697 // (the caller must decide whether to throw an exception or ignore the tuple) 00698 bool BaseModelGenerator::verifyEAtomAnswerTuple(RegistryPtr reg, 00699 const ExternalAtom& eatom, const Tuple& t) const 00700 { 00701 LOG_SCOPE(DBG, "vEAAT", false); 00702 LOG(DBG,"= verifyEAtomAnswerTuple for " << eatom << " and tuple <" << printManyToString<RawPrinter>(t, ", ", reg) << ">"); 00703 // check answer tuple, if it corresponds to pattern 00704 00705 if( t.size() != eatom.tuple.size() ) 00706 throw PluginError("External atom " + eatom.pluginAtom->getPredicate() + 00707 " returned tuple <" + printManyToString<RawPrinter>(t, ", ", reg) + "> of incompatible size."); 00708 00709 // pattern may contain variables and constants 00710 Tuple pattern(eatom.tuple); 00711 00712 // consecutively compare tuple term vs pattern term of same index: 00713 // * if variable appears throw exception (programming error, plugins may only return constants) 00714 // * if constant meets variable -> set all variables of same ID in pattern to that constant and continue verifying 00715 // * if constant meets other constant -> return false (mismatch) 00716 // * if constant meets same constant -> continue verifying 00717 // return true 00718 00719 const unsigned arity = t.size(); 00720 for(unsigned at = 0; at < arity; ++at) { 00721 if( t[at].isVariableTerm() ) 00722 throw PluginError("External atom " + eatom.pluginAtom->getPredicate() + 00723 " returned variable in result tuple <" + printManyToString<RawPrinter>(t, ", ", reg) + "> which is forbidden"); 00724 00725 if( pattern[at].isVariableTerm() ) { 00726 // set all variables to this constant and continue 00727 ID variable = pattern[at]; 00728 if( !variable.isAnonymousVariable() ) { 00729 for(unsigned i = at; i < arity; ++i) { 00730 if( pattern[i] == variable ) 00731 pattern[i] = t[at]; 00732 } 00733 } 00734 } 00735 else if( pattern[at].isNestedTerm() ) { 00736 // no explicit unification check; just assume that they unify 00737 } 00738 else if( pattern[at] != t[at] ) { 00739 // mismatch 00740 return false; 00741 } 00742 else { 00743 // ok, continue 00744 assert(t[at] == pattern[at]); 00745 } 00746 } 00747 00748 return true; 00749 } 00750 00751 00752 InterpretationPtr BaseModelGenerator::projectEAtomInputInterpretation(RegistryPtr reg, 00753 const ExternalAtom& eatom, InterpretationConstPtr full) const 00754 { 00755 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"BaseModelGen::projectEAII"); 00756 // we do this in general for the eatom 00757 //eatom.updatePredicateInputMask(); 00758 00759 InterpretationPtr ret; 00760 if( full == 0 ) 00761 ret.reset(new Interpretation(reg)); 00762 else 00763 ret.reset(new Interpretation(*full)); 00764 ret->getStorage() &= eatom.getPredicateInputMask()->getStorage(); 00765 return ret; 00766 } 00767 00768 00769 void BaseModelGenerator::buildEAtomInputTuples(RegistryPtr reg, 00770 const ExternalAtom& eatom, 00771 InterpretationConstPtr interpretation, 00772 InterpretationPtr inputs) const 00773 { 00774 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sid,"BaseModelGen::buildEAIT"); 00775 LOG_SCOPE(PLUGIN,"bEAIT",false); 00776 DBGLOG(DBG,"= buildEAtomInputTuples " << eatom); 00777 00778 // it must be true here 00779 assert(!!reg->eaInputTupleCache); 00780 EAInputTupleCache& eaitc = *reg->eaInputTupleCache; 00781 00782 // if there are no variables, there is no eatom.auxInputPredicate and this function should not be called 00783 assert(eatom.auxInputPredicate != ID_FAIL); 00784 00785 // otherwise find all aux input predicates that are true and extract their tuples 00786 Interpretation relevant(reg); 00787 relevant.getStorage() |= interpretation->getStorage() & eatom.getAuxInputMask()->getStorage(); 00788 Interpretation::TrueBitIterator it, it_end; 00789 boost::tie(it, it_end) = relevant.trueBits(); 00790 { 00791 for(;it != it_end; ++it) { 00792 IDAddress inputAtomBit = *it; 00793 00794 // lookup or create in cache 00795 Tuple& t = eaitc.lookupOrCreate(inputAtomBit); 00796 00797 if( t.empty() ) { 00798 // create it 00799 00800 const dlvhex::OrdinaryAtom& oatom = reg->ogatoms.getByAddress(inputAtomBit); 00801 00802 // add copy of original input tuple 00803 t = eatom.inputs; 00804 00805 // replace all occurances of variables with the corresponding predicates in auxinput 00806 for(unsigned idx = 0; idx < eatom.auxInputMapping.size(); ++idx) { 00807 // idx is the index of the argument to the auxiliary predicate 00808 // at 0 there is the auxiliary predicate 00809 ID replaceBy = oatom.tuple[idx+1]; 00810 // replaceBy is the ground term we will use instead of the input constant variable 00811 for(std::list<unsigned>::const_iterator it = eatom.auxInputMapping[idx].begin(); 00812 it != eatom.auxInputMapping[idx].end(); ++it) { 00813 // *it is the index of the input term that is a variable 00814 // (this also verifies that we do not overwrite a variable twice with different values) 00815 assert(t[*it].isTerm() && (t[*it].isVariableTerm() || t[*it].isNestedTerm())); 00816 t[*it] = replaceBy; 00817 } 00818 } 00819 DBGLOG(DBG,"after inserting auxiliary predicate inputs: input = " << printManyToString<RawPrinter>(t, ",", reg)); 00820 } 00821 00822 // signal to caller, that it should use the bit/tuple 00823 inputs->setFact(inputAtomBit); 00824 } 00825 } 00826 } 00827 00828 00829 // rewrite all eatoms in body tuple to auxiliary replacement atoms 00830 // store new body into convbody 00831 // (works recursively for aggregate atoms, 00832 // will create additional "auxiliary" aggregate atoms in registry) 00833 void BaseModelGeneratorFactory::convertRuleBody( 00834 ProgramCtx& ctx, const Tuple& body, Tuple& convbody) 00835 { 00836 assert(convbody.empty()); 00837 RegistryPtr reg = ctx.registry(); 00838 for(Tuple::const_iterator itlit = body.begin(); 00839 itlit != body.end(); ++itlit) { 00840 if( itlit->isAggregateAtom() ) { 00841 // recursively treat aggregates 00842 00843 // findout if aggregate contains external atoms 00844 const AggregateAtom& aatom = reg->aatoms.getByID(*itlit); 00845 AggregateAtom convaatom(aatom); 00846 convaatom.literals.clear(); 00847 convertRuleBody(ctx, aatom.literals, convaatom.literals); 00848 if( convaatom.literals != aatom.literals ) { 00849 // really create new aggregate atom 00850 convaatom.kind |= ID::PROPERTY_AUX; 00851 ID newaatomid = reg->aatoms.storeAndGetID(convaatom); 00852 convbody.push_back(ID::posLiteralFromAtom(newaatomid)); 00853 } 00854 else { 00855 // use original aggregate atom 00856 convbody.push_back(*itlit); 00857 } 00858 } 00859 else if( itlit->isExternalAtom() ) { 00860 bool naf = itlit->isNaf(); 00861 const ExternalAtom& eatom = reg->eatoms.getByID( 00862 ID::atomFromLiteral(*itlit)); 00863 DBGLOG(DBG,"rewriting external atom " << eatom << 00864 " literal with id " << *itlit); 00865 00866 // create replacement atom 00867 OrdinaryAtom replacement(ID::MAINKIND_ATOM | ID::PROPERTY_AUX | ID::PROPERTY_EXTERNALAUX); 00868 assert(!!eatom.pluginAtom); 00869 replacement.tuple.push_back( 00870 reg->getAuxiliaryConstantSymbol('r', 00871 eatom.pluginAtom->getPredicateID())); 00872 if (ctx.config.getOption("IncludeAuxInputInAuxiliaries") && eatom.auxInputPredicate != ID_FAIL) { 00873 replacement.tuple.push_back(eatom.auxInputPredicate); 00874 } 00875 replacement.tuple.insert(replacement.tuple.end(), 00876 eatom.inputs.begin(), eatom.inputs.end()); 00877 replacement.tuple.insert(replacement.tuple.end(), 00878 eatom.tuple.begin(), eatom.tuple.end()); 00879 00880 // bit trick: replacement is ground so far, by setting one bit we make it nonground 00881 bool ground = true; 00882 BOOST_FOREACH(ID term, replacement.tuple) { 00883 if( term.isVariableTerm() ) 00884 ground = false; 00885 } 00886 if( !ground ) 00887 replacement.kind |= ID::SUBKIND_ATOM_ORDINARYN; 00888 00889 ID idreplacement; 00890 if( ground ) 00891 idreplacement = reg->storeOrdinaryGAtom(replacement); 00892 else 00893 idreplacement = reg->storeOrdinaryNAtom(replacement); 00894 DBGLOG(DBG,"adding replacement atom " << idreplacement << " as literal"); 00895 convbody.push_back(ID::literalFromAtom(idreplacement, naf)); 00896 } 00897 else { 00898 DBGLOG(DBG,"adding original literal " << *itlit); 00899 convbody.push_back(*itlit); 00900 } 00901 } 00902 } 00903 00904 00905 // get rule 00906 // rewrite all eatoms in body to auxiliary replacement atoms 00907 // store and return id 00908 ID BaseModelGeneratorFactory::convertRule(ProgramCtx& ctx, ID ruleid) 00909 { 00910 RegistryPtr reg = ctx.registry(); 00911 if( !ruleid.doesRuleContainExtatoms() ) { 00912 DBGLOG(DBG,"not converting rule " << ruleid << " (does not contain extatoms)"); 00913 return ruleid; 00914 } 00915 00916 // we need to rewrite 00917 const Rule& rule = reg->rules.getByID(ruleid); 00918 #ifndef NDEBUG 00919 { 00920 std::stringstream s; 00921 RawPrinter printer(s, reg); 00922 printer.print(ruleid); 00923 DBGLOG(DBG,"rewriting rule " << s.str() << " from " << rule << 00924 " with id " << ruleid << " to auxiliary predicates"); 00925 } 00926 #endif 00927 00928 // copy it 00929 Rule newrule(rule); 00930 newrule.kind |= ID::PROPERTY_AUX; 00931 newrule.body.clear(); 00932 00933 // convert (recursively in aggregates) 00934 convertRuleBody(ctx, rule.body, newrule.body); 00935 00936 // store as rule 00937 ID newruleid = reg->storeRule(newrule); 00938 #ifndef NDEBUG 00939 { 00940 std::stringstream s; 00941 RawPrinter printer(s, reg); 00942 printer.print(newruleid); 00943 DBGLOG(DBG,"rewritten rule " << s.str() << " from " << newrule << 00944 " got id " << newruleid); 00945 } 00946 #endif 00947 return newruleid; 00948 } 00949 00950 00951 // adds for all external atoms with output variables which fail the strong safety check 00952 // a domain predicate to the rule body 00953 void BaseModelGeneratorFactory::addDomainPredicatesAndCreateDomainExplorationProgram(const ComponentGraph::ComponentInfo& ci, ProgramCtx& ctx, std::vector<ID>& idb, std::vector<ID>& deidb, std::vector<ID>& deidbInnerEatoms, const std::vector<ID>& outerEatoms) 00954 { 00955 00956 RegistryPtr reg = ctx.registry(); 00957 00958 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexground, "HEX grounder time"); 00959 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidadpacdep,"addDomainPredsAndCrDomExplProg"); 00960 00961 std::vector<ID> idbWithDomainPredicates; 00962 deidb.reserve(idb.size()); 00963 idbWithDomainPredicates.reserve(idb.size()); 00964 00965 // for all rules in the IDB 00966 BOOST_FOREACH (ID ruleid, idb) { 00967 00968 if( !ruleid.doesRuleContainExtatoms() ) { 00969 DBGLOG(DBG,"not processing rule " << ruleid << " (does not contain extatoms)"); 00970 idbWithDomainPredicates.push_back(ruleid); 00971 deidb.push_back(ruleid); 00972 continue; 00973 } 00974 00975 // add domain predicates for all external atoms which are relevant for de-safety 00976 const Rule& rule = reg->rules.getByID(ruleid); 00977 Rule ruleDom = rule; // this will be the original rule, but with additional domain atoms for each external atom 00978 // this will be the rule used for computing the domains: it contains the domain atom in the body and a guess of the external atom in the head 00979 Rule ruleExpl(rule.kind & (ID::ALL_ONES - ID::PROPERTY_RULE_EXTATOMS)); 00980 ruleExpl.head = rule.head; 00981 BOOST_FOREACH (ID b, rule.body) { 00982 if (!b.isExternalAtom()) { 00983 ruleExpl.body.push_back(b); 00984 } 00985 if (!b.isNaf() && b.isExternalAtom()) { 00986 const ExternalAtom& ea = reg->eatoms.getByID(b); 00987 00988 if (ctx.liberalSafetyChecker->isExternalAtomNecessaryForDomainExpansionSafety(b)) { 00989 bool isOuterEatom = (std::find(outerEatoms.begin(), outerEatoms.end(), ID::atomFromLiteral(b)) != outerEatoms.end()); 00990 00991 // print a warning if there is a nonmonotonic external atom which is necessary for de-safety, because this makes grounding really slow 00992 // (exponential in the number of nonmonotonic input atoms) 00993 if (ci.stratifiedLiterals.find(ruleid) == ci.stratifiedLiterals.end() || 00994 std::find(ci.stratifiedLiterals.at(ruleid).begin(), ci.stratifiedLiterals.at(ruleid).end(), b) == ci.stratifiedLiterals.at(ruleid).end()) { 00995 std::stringstream ss; 00996 RawPrinter printer(ss, reg); 00997 ss << "External atom "; 00998 printer.print(b); 00999 ss << " in rule " << std::endl; 01000 ss << " "; 01001 printer.print(ruleid); 01002 ss << std::endl; 01003 ss << " is nonmonotonic and necessary for safety. This can decrease grounding performance significantly." << std::endl; 01004 ss << " Consider using a different heuristics or ensure safty by other means, e.g., additional ordinary atoms which bound the output."; 01005 LOG(WARNING, ss.str()); 01006 } 01007 01008 // remember that this external atom was necessary for de-safety 01009 DBGLOG(DBG, "External atom " << b << " is necessary for de-safety"); 01010 deidbInnerEatoms.push_back(b); 01011 01012 if (isOuterEatom) { 01013 const ExternalAtom& eatom = reg->eatoms.getByID(b); 01014 01015 OrdinaryAtom replacement(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN | ID::PROPERTY_AUX | ID::PROPERTY_EXTERNALAUX); 01016 replacement.tuple.push_back(reg->getAuxiliaryConstantSymbol('r', eatom.predicate)); 01017 01018 if (ctx.config.getOption("IncludeAuxInputInAuxiliaries") && eatom.auxInputPredicate != ID_FAIL) { 01019 replacement.tuple.push_back(eatom.auxInputPredicate); 01020 } 01021 replacement.tuple.insert(replacement.tuple.end(), eatom.inputs.begin(), eatom.inputs.end()); 01022 replacement.tuple.insert(replacement.tuple.end(), eatom.tuple.begin(), eatom.tuple.end()); 01023 01024 ruleExpl.body.push_back(ID::posLiteralFromAtom(reg->storeOrdinaryAtom(replacement))); 01025 } 01026 else { 01027 OrdinaryAtom domainAtom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN | ID::PROPERTY_AUX); 01028 OrdinaryAtom chosenDomainAtom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN | ID::PROPERTY_AUX); 01029 OrdinaryAtom notChosenDomainAtom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN | ID::PROPERTY_AUX); 01030 domainAtom.tuple.push_back(reg->getAuxiliaryConstantSymbol('d', b)); 01031 // reuse auxiliaries for positive and negative replacements: they don't occur in the domain 01032 chosenDomainAtom.tuple.push_back(reg->getAuxiliaryConstantSymbol('r', b)); 01033 // exploration program anyway 01034 notChosenDomainAtom.tuple.push_back(reg->getAuxiliaryConstantSymbol('n', b)); 01035 if (ctx.config.getOption("IncludeAuxInputInAuxiliaries") && ea.auxInputPredicate != ID_FAIL) { 01036 domainAtom.tuple.push_back(ea.auxInputPredicate); 01037 chosenDomainAtom.tuple.push_back(ea.auxInputPredicate); 01038 notChosenDomainAtom.tuple.push_back(ea.auxInputPredicate); 01039 } 01040 BOOST_FOREACH (ID o2, ea.inputs) { 01041 domainAtom.tuple.push_back(o2); 01042 chosenDomainAtom.tuple.push_back(o2); 01043 notChosenDomainAtom.tuple.push_back(o2); 01044 } 01045 BOOST_FOREACH (ID o2, ea.tuple) { 01046 domainAtom.tuple.push_back(o2); 01047 chosenDomainAtom.tuple.push_back(o2); 01048 notChosenDomainAtom.tuple.push_back(o2); 01049 } 01050 ID domainAtomID = reg->storeOrdinaryNAtom(domainAtom); 01051 ID chosenDomainAtomID = reg->storeOrdinaryNAtom(chosenDomainAtom); 01052 ID notChosenDomainAtomID = reg->storeOrdinaryNAtom(notChosenDomainAtom); 01053 01054 ruleDom.body.push_back(ID::posLiteralFromAtom(domainAtomID)); 01055 ruleExpl.body.push_back(ID::posLiteralFromAtom(chosenDomainAtomID)); 01056 01057 // create a rule p(X) v n(X) :- d(X) for each domain atom d 01058 // this nondeterminisim is necessary to make the grounding exhaustive; otherwise the grounder may optimize the grounding too much and we are not aware of relevant atoms 01059 Rule choosingRule(ID::MAINKIND_RULE | ID::PROPERTY_RULE_DISJ); 01060 choosingRule.head.push_back(chosenDomainAtomID); 01061 choosingRule.head.push_back(notChosenDomainAtomID); 01062 choosingRule.body.push_back(ID::posLiteralFromAtom(domainAtomID)); 01063 ID choosingRuleID = reg->storeRule(choosingRule); 01064 deidb.push_back(choosingRuleID); 01065 { 01066 std::stringstream s; 01067 RawPrinter printer(s, reg); 01068 s << "adding choosing rule "; 01069 printer.print(choosingRuleID); 01070 s << " for external atom " << b; 01071 DBGLOG(DBG, s.str()); 01072 } 01073 } 01074 } 01075 } 01076 } 01077 01078 // add rule with domain predicates to IDB 01079 ID ruleDomID = reg->storeRule(ruleDom); 01080 idbWithDomainPredicates.push_back(ruleDomID); 01081 #ifndef NDEBUG 01082 { 01083 std::stringstream s; 01084 RawPrinter printer(s, reg); 01085 s << "adding domain predicates: rewriting rule "; 01086 printer.print(ruleid); 01087 s << " to "; 01088 printer.print(ruleDomID); 01089 } 01090 #endif 01091 01092 // create domain exploration rule (if necessary) 01093 if (ruleExpl.head.size() > 0 || ruleExpl.body.size() > 0) { 01094 ID ruleExplID = reg->storeRule(ruleExpl); 01095 deidb.push_back(ruleExplID); 01096 #ifndef NDEBUG 01097 { 01098 std::stringstream s; 01099 RawPrinter printer(s, reg); 01100 s << "Creating domain-exploration rule "; 01101 printer.print(ruleExplID); 01102 DBGLOG(DBG, s.str()); 01103 } 01104 #endif 01105 } 01106 } 01107 01108 // update the original IDB 01109 idb = idbWithDomainPredicates; 01110 } 01111 01112 01113 InterpretationConstPtr BaseModelGenerator::computeExtensionOfDomainPredicates(const ComponentGraph::ComponentInfo& ci, ProgramCtx& ctx, InterpretationConstPtr edb, std::vector<ID>& deidb, std::vector<ID>& deidbInnerEatoms, bool enumerateNonmonotonic) 01114 { 01115 01116 RegistryPtr reg = ctx.registry(); 01117 01118 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidcedp,"computeExtensionOfDomainPreds"); 01119 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidhexground, "HEX grounder time"); 01120 01121 InterpretationPtr domintr = InterpretationPtr(new Interpretation(reg)); 01122 domintr->getStorage() |= edb->getStorage(); 01123 01124 DBGLOG(DBG, "Computing fixpoint of extensions of domain predicates"); 01125 DBGLOG(DBG, "" << deidbInnerEatoms.size() << " inner external atoms are necessary for establishing de-safety"); 01126 01127 // if there are no inner external atoms, then there is nothing to do 01128 if (deidbInnerEatoms.size() == 0) return InterpretationPtr(new Interpretation(reg)); 01129 01130 InterpretationPtr auxinputs = InterpretationPtr(new Interpretation(reg)); 01131 InterpretationPtr herbrandBase = InterpretationPtr(new Interpretation(reg)); 01132 InterpretationPtr oldherbrandBase = InterpretationPtr(new Interpretation(reg)); 01133 herbrandBase->getStorage() |= edb->getStorage(); 01134 do { 01135 oldherbrandBase->getStorage() = herbrandBase->getStorage(); 01136 01137 DBGLOG(DBG, "Loop with herbrandBase=" << *herbrandBase); 01138 01139 // ground program 01140 OrdinaryASPProgram program(reg, deidb, domintr, ctx.maxint); 01141 GenuineGrounderPtr grounder = GenuineGrounder::getInstance(ctx, program); 01142 01143 // retrieve the Herbrand base 01144 if (!!grounder->getGroundProgram().mask) { 01145 herbrandBase->getStorage() |= (grounder->getGroundProgram().edb->getStorage() - grounder->getGroundProgram().mask->getStorage()); 01146 } 01147 else { 01148 herbrandBase->getStorage() |= grounder->getGroundProgram().edb->getStorage(); 01149 } 01150 BOOST_FOREACH (ID rid, grounder->getGroundProgram().idb) { 01151 const Rule& r = reg->rules.getByID(rid); 01152 BOOST_FOREACH (ID h, r.head) 01153 if (!grounder->getGroundProgram().mask || !grounder->getGroundProgram().mask->getFact(h.address)) herbrandBase->setFact(h.address); 01154 BOOST_FOREACH (ID b, r.body) 01155 if (!grounder->getGroundProgram().mask || !grounder->getGroundProgram().mask->getFact(b.address)) herbrandBase->setFact(b.address); 01156 } 01157 01158 // evaluate inner external atoms 01159 BaseModelGenerator::IntegrateExternalAnswerIntoInterpretationCB cb(herbrandBase); 01160 BOOST_FOREACH (ID eaid, deidbInnerEatoms) { 01161 const ExternalAtom& ea = reg->eatoms.getByID(eaid); 01162 01163 // remove all atoms over antimonotonic parameters from the input interpretation (both in standard and in higher-order notation) 01164 // in order to maximize the output; 01165 // for nonmonotonic input atoms, enumerate all (exponentially many) possible assignments 01166 boost::unordered_map<IDAddress, bool> nonmonotonicinput; 01167 InterpretationPtr input(new Interpretation(reg)); 01168 input->add(*herbrandBase); 01169 ea.updatePredicateInputMask(); 01170 bm::bvector<>::enumerator en = ea.getPredicateInputMask()->getStorage().first(); 01171 bm::bvector<>::enumerator en_end = ea.getPredicateInputMask()->getStorage().end(); 01172 while (en < en_end) { 01173 const OrdinaryAtom& ogatom = reg->ogatoms.getByAddress(*en); 01174 01175 for (uint32_t i = 0; i < ea.inputs.size(); ++i) { 01176 if (ea.pluginAtom->getInputType(i) == PluginAtom::PREDICATE && 01177 ea.getExtSourceProperties().isAntimonotonic(i) && 01178 ogatom.tuple[0] == ea.inputs[i]) { 01179 DBGLOG(DBG, "Setting " << *en << " to false because it is an antimonotonic input atom"); 01180 input->clearFact(*en); 01181 } 01182 if (ea.pluginAtom->getInputType(i) == PluginAtom::PREDICATE && 01183 !ea.getExtSourceProperties().isAntimonotonic(i) && 01184 !ea.getExtSourceProperties().isMonotonic(i) && 01185 ogatom.tuple[0] == ea.inputs[i]) { 01186 // if the predicate is defined in this component, enumerate all possible assignments 01187 if (ci.predicatesDefinedInComponent.count(ea.inputs[i]) > 0) { 01188 DBGLOG(DBG, "Must guess all assignments to " << *en << " because it is a nonmonotonic and unstratified input atom"); 01189 nonmonotonicinput[*en] = false; 01190 } 01191 // otherwise: take the truth value from the edb 01192 else { 01193 if (!edb->getFact(*en)) { 01194 DBGLOG(DBG, "Setting " << *en << " to false because it is stratified and false in the edb"); 01195 input->clearFact(*en); 01196 } 01197 } 01198 } 01199 } 01200 en++; 01201 } 01202 01203 typedef std::pair<IDAddress, bool> Pair; 01204 if (!enumerateNonmonotonic) { 01205 // evalute external atom 01206 DBGLOG(DBG, "Evaluating external atom " << eaid << " under " << *input << " (do not enumerate nonmonotonic input assignments due to user request)"); 01207 BOOST_FOREACH (Pair p, nonmonotonicinput) input->clearFact(p.first); 01208 evaluateExternalAtom(ctx, eaid, input, cb); 01209 } 01210 else { 01211 DBGLOG(DBG, "Enumerating nonmonotonic input assignments to " << eaid); 01212 bool allOnes; 01213 do { 01214 // set nonmonotonic input 01215 allOnes = true; 01216 BOOST_FOREACH (Pair p, nonmonotonicinput) { 01217 if (p.second) input->setFact(p.first); 01218 else { 01219 input->clearFact(p.first); 01220 allOnes = false; 01221 } 01222 } 01223 01224 // evalute external atom 01225 DBGLOG(DBG, "Evaluating external atom " << eaid << " under " << *input); 01226 evaluateExternalAtom(ctx, eaid, input, cb); 01227 01228 // enumerate next assignment to nonmonotonic input atoms 01229 if (!allOnes) { 01230 std::vector<IDAddress> clear; 01231 BOOST_FOREACH (Pair p, nonmonotonicinput) { 01232 if (p.second) clear.push_back(p.first); 01233 else { 01234 nonmonotonicinput[p.first] = true; 01235 break; 01236 } 01237 } 01238 BOOST_FOREACH (IDAddress c, clear) nonmonotonicinput[c] = false; 01239 } 01240 }while(!allOnes); 01241 01242 DBGLOG(DBG, "Enumerated all nonmonotonic input assignments to " << eaid); 01243 } 01244 } 01245 01246 // translate new EA-replacements to domain atoms 01247 bm::bvector<>::enumerator en = herbrandBase->getStorage().first(); 01248 bm::bvector<>::enumerator en_end = herbrandBase->getStorage().end(); 01249 while (en < en_end) { 01250 ID id = reg->ogatoms.getIDByAddress(*en); 01251 if (id.isExternalAuxiliary()) { 01252 DBGLOG(DBG, "Converting atom with address " << *en); 01253 01254 const OrdinaryAtom& ogatom = reg->ogatoms.getByAddress(*en); 01255 BOOST_FOREACH (ID eaid, deidbInnerEatoms) { 01256 const ExternalAtom ea = reg->eatoms.getByID(eaid); 01257 if (ea.predicate == reg->getIDByAuxiliaryConstantSymbol(ogatom.tuple[0])) { 01258 01259 OrdinaryAtom domatom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG | ID::PROPERTY_AUX); 01260 domatom.tuple.push_back(reg->getAuxiliaryConstantSymbol('d', eaid)); 01261 int io = 1; 01262 // if (ea.auxInputPredicate != ID_FAIL && ctx.config.getOption("IncludeAuxInputInAuxiliaries")) io = 2; 01263 for (uint32_t i = io; i < ogatom.tuple.size(); ++i) { 01264 domatom.tuple.push_back(ogatom.tuple[i]); 01265 } 01266 domintr->setFact(reg->storeOrdinaryGAtom(domatom).address); 01267 } 01268 } 01269 } 01270 en++; 01271 } 01272 herbrandBase->getStorage() |= domintr->getStorage(); 01273 DBGLOG(DBG, "Domain extension interpretation (intermediate result, including EDB): " << *domintr); 01274 }while(herbrandBase->getStorage().count() != oldherbrandBase->getStorage().count()); 01275 01276 domintr->getStorage() -= edb->getStorage(); 01277 DBGLOG(DBG, "Domain extension interpretation (final result): " << *domintr); 01278 return domintr; 01279 } 01280 01281 01282 DLVHEX_NAMESPACE_END 01283 01284 // vim:expandtab:ts=4:sw=4: 01285 // mode: C++ 01286 // End: 01287