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