dlvhex  2.5.0
src/PluginInterface.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 
00038 #include "dlvhex2/PluginInterface.h"
00039 
00040 #ifdef HAVE_CONFIG_H
00041 #  include "config.h"
00042 #endif
00043 
00044 #include "dlvhex2/Registry.h"
00045 #include "dlvhex2/ProgramCtx.h"
00046 #include "dlvhex2/GenuineSolver.h"
00047 #include "dlvhex2/Term.h"
00048 #include "dlvhex2/ID.h"
00049 #include "dlvhex2/Term.h"
00050 #include "dlvhex2/ID.h"
00051 #include "dlvhex2/Benchmarking.h"
00052 #include "dlvhex2/HexParser.h"
00053 #include "dlvhex2/ExternalLearningHelper.h"
00054 
00055 DLVHEX_NAMESPACE_BEGIN
00056 
00057 #if 0
00058 bool PluginAtom::Query::operator<(const Query& other) const
00059 {
00060     /*
00061     return
00062       ( interpretation < other.interpretation ) ||
00063       ( interpretation == other.interpretation &&
00064         input < other.input ) ||
00065       ( interpretation == other.interpretation &&
00066         input == other.input &&
00067         pattern < other.pattern );
00068         */
00069 }
00070 #endif
00071 
00072 void PluginAtom::Query::assign(const PluginAtom::Query& q2){
00073     ctx = q2.ctx;
00074     interpretation.reset(); assigned.reset(); changed.reset(); predicateInputMask.reset();
00075     if (!!q2.interpretation) { InterpretationPtr interpretation(new Interpretation(q2.ctx->registry())); interpretation->add(*q2.interpretation); this->interpretation = interpretation; }
00076     if (!!q2.assigned) { InterpretationPtr assigned(new Interpretation(q2.ctx->registry())); assigned->add(*q2.assigned); this->assigned = assigned; }
00077     if (!!q2.changed) { InterpretationPtr changed(new Interpretation(q2.ctx->registry())); changed->add(*q2.changed); this->changed = changed; }
00078     input = q2.input;
00079     pattern = q2.pattern;
00080     eatomID = q2.eatomID;
00081     if (!!q2.predicateInputMask) { InterpretationPtr predicateInputMask(new Interpretation(q2.ctx->registry())); predicateInputMask->add(*q2.predicateInputMask); this->predicateInputMask = predicateInputMask; }
00082 }
00083 
00084 bool PluginAtom::Query::operator==(const Query& other) const
00085 {
00086     return
00087         (input == other.input) &&
00088         (pattern == other.pattern) &&
00089         (
00090         (interpretation == other.interpretation) ||
00091         (interpretation != 0 && other.interpretation != 0 && *interpretation == *other.interpretation)
00092     ) &&
00093         (
00094         (assigned == other.assigned) ||
00095         (assigned != 0 && other.assigned != 0 && *assigned == *other.assigned)
00096     ) &&
00097     (
00098         (predicateInputMask == other.predicateInputMask) ||
00099         (predicateInputMask != 0 && other.predicateInputMask != 0 && *predicateInputMask == *other.predicateInputMask)
00100     );
00101 }
00102 
00103 
00104 // hash function for QueryAnswerCache
00105 std::size_t hash_value(const PluginAtom::Query& q)
00106 {
00107     std::size_t seed = 0;
00108     boost::hash_combine(seed, q.input);
00109     //LOG("hash_combine inp " << printrange(q.input) << " yields " << seed);
00110     boost::hash_combine(seed, q.pattern);
00111     //LOG("hash_combine pat " << printrange(q.pattern) << " yields " << seed);
00112     // TODO: can we take hash of pointer to interpretation here?
00113     if( q.interpretation == 0 ) {
00114         boost::hash_combine(seed, 0);
00115     }
00116     else {
00117         // TODO: outsource this
00118         //boost::hash_combine(seed, q.interpretation->getStorage());
00119         const Interpretation::Storage& bits = q.interpretation->getStorage();
00120         for(Interpretation::Storage::enumerator en = bits.first();
00121         en != bits.end(); ++en) {
00122             boost::hash_combine(seed, *en);
00123             //LOG("hash_combine at " << *en << " yields " << seed);
00124         }
00125     }
00126     //LOG("hash_combine returning " << seed);
00127     return seed;
00128 }
00129 
00130 PluginAtom::Answer::Answer():
00131 output(new std::vector<Tuple>),
00132 unknown(new std::vector<Tuple>),
00133 used(false)
00134 {
00135 }
00136 
00137 void
00138 PluginAtom::addInputPredicate(bool nameIsRelevant)
00139 {
00140     // throw error if last input term was tuple
00141     if (inputType.size() > 0)
00142         if (inputType.back() == TUPLE)
00143             throw GeneralError("Tuple inputs must be specified last in input list");
00144 
00145     inputType.push_back(PREDICATE);
00146 
00147     if (!nameIsRelevant) prop.predicateParameterNameIndependence.insert(inputType.size() - 1);
00148 
00149     if (allmonotonic) prop.monotonicInputPredicates.insert(inputType.size() - 1);
00150 }
00151 
00152 
00153 void
00154 PluginAtom::addInputConstant()
00155 {
00156     // throw error if last input term was tuple
00157     if (inputType.size() > 0)
00158         if (inputType.back() == TUPLE)
00159             throw GeneralError("Tuple inputs must be specified last in input list");
00160 
00161     inputType.push_back(CONSTANT);
00162 }
00163 
00164 
00165 void
00166 PluginAtom::addInputTuple()
00167 {
00168     inputType.push_back(TUPLE);
00169 }
00170 
00171 
00172 int
00173 PluginAtom::getInputArity() const
00174 {
00175     return inputType.size();
00176 }
00177 
00178 
00179 int
00180 PluginAtom::getOutputArity() const
00181 {
00182     return outputSize;
00183 }
00184 
00185 
00186 bool
00187 PluginAtom::checkInputArity(const unsigned arity) const
00188 {
00189     bool ret = (inputType.size() == arity);
00190 
00191     if (!inputType.empty()) {
00192         return inputType.back() == TUPLE ? true : ret;
00193     }
00194     else {
00195         return ret;
00196     }
00197 }
00198 
00199 
00200 void
00201 PluginAtom::setOutputArity(const unsigned arity)
00202 {
00203     outputSize = arity;
00204 }
00205 
00206 
00207 bool
00208 PluginAtom::checkOutputArity(const ExtSourceProperties& prop, const unsigned arity) const
00209 {
00210     return prop.hasVariableOutputArity() || (arity == outputSize);
00211 }
00212 
00213 
00214 /*
00215 void PluginAtom::retrieveCached(const Query& query, Answer& answer)
00216 {
00217   DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidrc,"PluginAtom retrieveCached");
00218   // Cache answer for queries which were already done once:
00219   //
00220   // The most efficient way would be:
00221   // * use cache for same inputSet + same *inputi + more specific pattern
00222   // * store new cache for new inputSet/*inputi combination or unrelated (does not unify) pattern
00223   // * replace cache for existing inputSet/*inputi combination and less specific (unifies in one direction) pattern
00224   //
00225   // The currently implemented "poor (wo)man's version" is:
00226   // * store answers in cache with queries as keys, disregard relations between patterns
00228 
00229   boost::mutex::scoped_lock lock(cacheMutex);
00230 
00231   //LOG("before queryAnswerCache");
00232   Answer& ans = queryAnswerCache[query];
00233   //LOG("after queryAnswerCache");
00234   if( ans.hasBeenUsed() )
00235   {
00236     // answer was not default constructed
00237     // -> use cache
00238     answer = ans;
00239   }
00240   else
00241   {
00242     // answer was default constructed
00243     // -> retrieve and replace in cache
00244     {
00245       DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"PluginAtom retrieve");
00246       retrieve(query, ans);
00247       // if there was no answer, perhaps it has never been used, so we use it manually
00248       ans.use();
00249     }
00250     answer = ans;
00251   }
00252 }
00253 */
00254 
00255 bool PluginAtom::retrieveFacade(const Query& query, Answer& answer, NogoodContainerPtr nogoods, bool useCache)
00256 {
00257     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidrf,"PluginAtom retrieveFacade");
00258     bool fromCache = false;
00259 
00260     // split the query
00261     ExtSourceProperties emptyProp;
00262     const ExtSourceProperties& prop = (query.eatomID != ID_FAIL ? registry->eatoms.getByID(query.eatomID).getExtSourceProperties() : emptyProp);
00263 
00264     DBGLOG(DBG, "Splitting query");
00265     std::vector<Query> atomicQueries = splitQuery(query, prop);
00266     DBGLOG(DBG, "Got " << atomicQueries.size() << " atomic queries");
00267     BOOST_FOREACH (Query atomicQuery, atomicQueries) {
00268         Answer atomicAnswer;
00269         bool subqueryFromCache;
00270         if (useCache) {
00271             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"PluginAtom retrieveCached");
00272             subqueryFromCache = retrieveCached(atomicQuery, atomicAnswer, query.ctx->config.getOption("ExternalLearningUser") ? nogoods : NogoodContainerPtr());
00273         }
00274         else {
00275             replacements->updateMask();
00276             subqueryFromCache = false;
00277 
00278             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"PluginAtom retrieve");
00279             retrieve(atomicQuery, atomicAnswer, query.ctx->config.getOption("ExternalLearningUser") ? nogoods : NogoodContainerPtr());
00280         }
00281 
00282         // learn only if the query was not answered from cache (otherwise also the nogoods come from the cache)
00283         if (!subqueryFromCache) {
00284             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"retrieveFacade Learning");
00285             if (!!nogoods && query.ctx->config.getOption("ExternalLearningIOBehavior")) ExternalLearningHelper::learnFromInputOutputBehavior(atomicQuery, atomicAnswer, prop, nogoods);
00286             if (!!nogoods && query.ctx->config.getOption("ExternalLearningFunctionality") && prop.isFunctional()) ExternalLearningHelper::learnFromFunctionality(atomicQuery, atomicAnswer, prop, otuples, nogoods);
00287         }
00288 
00289         // overall answer is the union of the atomic answers
00290         DBGLOG(DBG, "Atomic query delivered " << atomicAnswer.get().size() << " tuples");
00291         answer.get().insert(answer.get().end(), atomicAnswer.get().begin(), atomicAnswer.get().end());
00292         answer.getUnknown().insert(answer.getUnknown().end(), atomicAnswer.getUnknown().begin(), atomicAnswer.getUnknown().end());
00293 
00294         // query counts as answered from cache if at least one subquery was answered from cache
00295         fromCache |= subqueryFromCache;
00296     }
00297 
00298     {
00299         DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"retrieveFacade neg. learning");
00300         if (!!nogoods && query.ctx->config.getOption("ExternalLearningNeg")) ExternalLearningHelper::learnFromNegativeAtoms(query, answer, prop, nogoods);
00301     }
00302 
00303     return fromCache;
00304 }
00305 
00306 
00307 bool PluginAtom::retrieveCached(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
00308 {
00309     DBGLOG(DBG, "Retrieve with learning, pointer to nogood container: " << (!nogoods ? "not " : "") << "available" );
00310 
00311     DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidrc,"PluginAtom retrieveCached");
00312     // Cache answer for queries which were already done once:
00313     //
00314     // The most efficient way would be:
00315     // * use cache for same inputSet + same *inputi + more specific pattern
00316     // * store new cache for new inputSet/*inputi combination or unrelated (does not unify) pattern
00317     // * replace cache for existing inputSet/*inputi combination and less specific (unifies in one direction) pattern
00318     //
00319     // The currently implemented "poor (wo)man's version" is:
00320     // * store answers in cache with queries as keys, disregard relations between patterns
00322 
00323 
00324     // Remark: Note that cache entries for nogoods must not be reused if the set of ground atoms in the registry (which might occur as input to the external atom) was expanded,
00325     //         even if the actual input (true atoms) to the external atom is the same.
00326     //         This is because negative atoms might be part of the premise part in the learned nogood.
00327     //
00328     // Example:
00329     //
00330     // Unit 1:
00331     //    d(a) v -d(a).
00332     //    -d(b) v d(b).
00333     //
00334     // Unit 2:
00335     //    p(X) :- &id[d](X).
00336     //      (where &id copies the extension of d to the output)
00337     //
00338     // Unit 3:
00339     //    q(X) :- &ext[p](X).
00340     //    :- q(X).
00341     //      (where &ext outputs {z} for input {p(a)} and {} otherwise)
00342     //
00343     // Suppose the guess of the atoms is in the order of occurrence in the program.
00344     //
00345     // Then the first guess in Unit 1 is {d(a), -d(b)}. Unit 2 derives then p(a) but not p(b); at this point p(b) is not even in the registry!
00346     // Then the third unit learns { T p(a), F &ext[p](z) }, i.e., { T p(a) } implies &ext[p](z).
00347     // Despite the absence of p(b) in the interpretation and the registry, F p(b) is conceptually part of the premise because {p(a), p(b)} would lead to the empty output.
00348     // Nevertheless the nogood is still ok for unit 3 because p(b) is implicitly fixed to false.
00349     //
00350     // However, because the learned nogoods are cached, they could be reused at some later point after p(b) was introduced. But then the nogood is not correct anymore
00351     // because F p(b) needs to be explicitly checked. In particular, the guess {d(a), d(b)} leads to the introduction of p(b) in Unit 2.
00352     // From this point, the nogood { T p(a), F &ext[p](z) } should not be retrievable anymore.
00353     // 
00354     // However, as Query::predicateInputMask changes whenever the set of ground atoms in the registry is expanded
00355     // (which might occur as input), comparing predicateInputMask in Query::operator==() should guarantee that the cache entry is not reused anymore
00356     // (actually, comparing the sizes of predicateInputMask suffices as predicateInputMask can only increase but not decrease when the registry is expanded).
00357 
00358     boost::mutex::scoped_lock lock(cacheMutex);
00359 InterpretationPtr emp(new Interpretation(query.ctx->registry()));
00360     typedef std::pair<Answer, SimpleNogoodContainerPtr> CacheEntryType;
00361 
00362     DLVHEX_BENCHMARK_REGISTER_AND_START(sidcl,"PluginAtom cache lookup");
00363     CacheEntryType& ans = queryAnswerNogoodCache[query];
00364     DLVHEX_BENCHMARK_STOP(sidcl);
00365     if( ans.first.hasBeenUsed()) {
00366         DBGLOG(DBG, "Answering from cache");
00367 
00368         // check if there are cached nogoods
00369         if (nogoods) {
00370             if (ans.second) {
00371                 DBGLOG(DBG, "Found " << ans.second->getNogoodCount() << " cached nogoods");
00372         // answer was not default -> use
00373         answer = ans.first;
00374                 // return cached nogoods
00375                 for (int i = 0; i < ans.second->getNogoodCount(); ++i) nogoods->addNogood(ans.second->getNogood(i));
00376                 return true;             // answered from cache
00377             }
00378             else {
00379                 DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"PluginAtom retrieve from retrieveCached");
00380                 // answer is cached but no nogoods: reevaluate and return nogoods
00381                 DBGLOG(DBG, "No cached nogoods --> reevaluate");
00382                 queryAnswerNogoodCache[query].first = Answer();
00383                 ans.second.reset(new SimpleNogoodContainer());
00384                 retrieve(query, ans.first, ans.second);
00385                 for (int i = 0; i < ans.second->getNogoodCount(); ++i) nogoods->addNogood(ans.second->getNogood(i));
00386         // answer was not default -> use
00387         answer = ans.first;
00388                 return false;             // not (fully) answered from cache
00389             }
00390         }else{
00391         // answer was not default -> use
00392         answer = ans.first;
00393         return true;
00394     }
00395     }
00396     else {
00397         {
00398             DBGLOG(DBG, "Answering by evaluation");
00399             // answer was default constructed
00400             // -> retrieve and replace in cache
00401             DLVHEX_BENCHMARK_REGISTER_AND_SCOPE(sidr,"PluginAtom retrieve");
00402 
00403             // make sure that the cache uses an in-depth copy of the query (otherwise the cache might change with the assignment)
00404             queryAnswerNogoodCache.erase(queryAnswerNogoodCache.find(query));
00405             Query queryc = query;
00406             queryc.assign(query);
00407             CacheEntryType& ans = queryAnswerNogoodCache[queryc]; // shadows above ans!
00408 
00409             if (nogoods) {
00410                 assert(!ans.second);
00411 
00412                 ans.second.reset(new SimpleNogoodContainer());
00413                 retrieve(queryc, ans.first, ans.second);
00414                 for (int i = 0; i < ans.second->getNogoodCount(); ++i) nogoods->addNogood(ans.second->getNogood(i));
00415                 answer = ans.first;
00416             }
00417             else {
00418                 retrieve(queryc, ans.first, NogoodContainerPtr());
00419                 answer = ans.first;
00420             }
00421 
00422             // if there was no answer, perhaps it has never been used, so we use it manually
00423             ans.first.use();
00424         }
00425         return false;            // not answered from cache
00426     }
00427 }
00428 
00429 
00430 void PluginAtom::retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods)
00431 {
00432     DBGLOG(DBG, "Default implementation of PluginAtom::retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods): delegating the call to PluginAtom::retrieve(const Query& query, Answer& answer)");
00433     retrieve(query, answer);
00434 }
00435 
00436 
00437 void PluginAtom::retrieve(const Query& query, Answer& answer)
00438 {
00439     DBGLOG(DBG, "Default implementation of PluginAtom::retrieve(const Query& query, Answer& answer): doing nothing");
00440 }
00441 
00442 
00443 void PluginAtom::learnSupportSets(const Query&, NogoodContainerPtr nogoods)
00444 {
00445     assert(prop.providesSupportSets() && "This external source does not provide support sets");
00446 }
00447 
00448 
00449 std::vector<PluginAtom::Query> PluginAtom::splitQuery(const Query& query, const ExtSourceProperties& prop)
00450 {
00451 
00452     std::vector<Query> atomicQueries;
00453     if (query.eatomID != ID_FAIL && (prop.isLinearOnAtomLevel() || prop.isLinearOnTupleLevel()) && query.ctx->config.getOption("ExternalLearningLinearity")) {
00454     const ExternalAtom& eatom = query.ctx->registry()->eatoms.getByID(query.eatomID);
00455 
00456         DBGLOG(DBG, "Splitting query by exploiting linearity");
00457 
00458         if (prop.isLinearOnAtomLevel()) {
00459             bm::bvector<>::enumerator en = eatom.getPredicateInputMask()->getStorage().first();
00460             bm::bvector<>::enumerator en_end = eatom.getPredicateInputMask()->getStorage().end();
00461             while (en < en_end) {
00462                 DBGLOG(DBG, "Creating partial query for input atom " << *en);
00463                 // create a partial query only for this input atom
00464                 Query qa = query;
00465                 qa.predicateInputMask = InterpretationPtr(new Interpretation(query.interpretation->getRegistry()));
00466                 qa.predicateInputMask->setFact(*en);
00467                 atomicQueries.push_back(qa);
00468 
00469                 en++;
00470             }
00471         }
00472         if (prop.isLinearOnTupleLevel()) {
00473             // collect all tuples
00474             bm::bvector<>::enumerator en = eatom.getPredicateInputMask()->getStorage().first();
00475             bm::bvector<>::enumerator en_end = eatom.getPredicateInputMask()->getStorage().end();
00476 
00477             // extract tuples from atoms in input interpretation
00478             std::set<Tuple> tuples;
00479             while (en < en_end) {
00480                 const OrdinaryAtom& atom = query.interpretation->getRegistry()->ogatoms.getByAddress(*en);
00481 
00482                 Tuple t;
00483                 for (uint32_t i = 1; i < atom.tuple.size(); i++) {
00484                     t.push_back(atom.tuple[i]);
00485                 }
00486                 tuples.insert(t);
00487 
00488                 en++;
00489             }
00490 
00491             // check if all input atoms have the same arity
00492             int arity = -1;
00493             bool split = true;
00494             BOOST_FOREACH (Tuple t, tuples) {
00495                 if (arity != -1 && t.size() != arity) {
00496                     split = false;
00497                     break;
00498                 }
00499                 arity = t.size();
00500             }
00501 
00502             if (split) {
00503                 // create for each tuple a subquery
00504                 BOOST_FOREACH (Tuple t, tuples) {
00505                     // create a partial query only for this input atom
00506                     #ifndef NDEBUG
00507                     std::stringstream ss;
00508                     bool first = true;
00509                     BOOST_FOREACH (ID id, t) {
00510                         if (!first) ss << ", ";
00511                         ss << id;
00512                         first = false;
00513                     }
00514                     DBGLOG(DBG, "Creating partial query for input tuple " << ss.str());
00515                     #endif
00516                     Query qa = query;
00517                     qa.predicateInputMask = InterpretationPtr(new Interpretation(query.interpretation->getRegistry()));
00518                     for (uint32_t parIndex = 0; parIndex < qa.input.size(); parIndex++) {
00519                         if (getInputType(parIndex) == PREDICATE) {
00520                             // assemble input atom over this tuple and predicate parameter
00521                             OrdinaryAtom atom(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG);
00522                             atom.tuple.push_back(qa.input[parIndex]);
00523                             BOOST_FOREACH (ID p, t) atom.tuple.push_back(p);
00524                             ID atomID = query.interpretation->getRegistry()->storeOrdinaryGAtom(atom);
00525                             DBGLOG(DBG, "Input atom: " << atomID);
00526 
00527                             if (eatom.getPredicateInputMask()->getFact(atomID.address)) {
00528                                 qa.predicateInputMask->setFact(atomID.address);
00529                             }
00530                         }
00531                     }
00532                     atomicQueries.push_back(qa);
00533                 }
00534             }
00535             else {
00536                 DBGLOG(DBG, "Do not split query because not all input atoms have the same arity");
00537                 atomicQueries.push_back(query);
00538             }
00539         }
00540 
00541         // for all atomic queries, remove input facts which are not in the predicate input
00542         for (uint32_t i = 0; i < atomicQueries.size(); ++i) {
00543             Query& q = atomicQueries[i];
00544             InterpretationPtr intr = InterpretationPtr(new Interpretation(q.interpretation->getRegistry()));
00545             intr->add(*q.interpretation);
00546             intr->getStorage() &= q.predicateInputMask->getStorage();
00547             q.interpretation = intr;
00548             DBGLOG(DBG, "Constructed atomic query (mask: " << *q.predicateInputMask << ", interpretation: " << *q.interpretation << ")");
00549         }
00550     }
00551     else {
00552         DBGLOG(DBG, "Do not split query");
00553         atomicQueries.push_back(query);
00554     }
00555 
00556     return atomicQueries;
00557 }
00558 
00559 
00560 void PluginAtom::generalizeNogood(Nogood ng, ProgramCtx* ctx, NogoodContainerPtr nogoods)
00561 {
00562 
00563     if (!ng.isGround()) return;
00564 
00565     DBGLOG(DBG, "PluginAtom::generalizeNogood");
00566 
00567     // find the auxiliary in the nogood
00568     ID patternID = ID_FAIL;
00569     BOOST_FOREACH (ID l, ng) {
00570         const OrdinaryAtom& lAtom = ctx->registry()->ogatoms.getByAddress(l.address);
00571 
00572         if (ctx->registry()->ogatoms.getIDByAddress(l.address).isExternalAuxiliary() && ctx->registry()->getIDByAuxiliaryConstantSymbol(lAtom.tuple[0]) == getPredicateID()) {
00573             patternID = l;
00574             break;
00575         }
00576     }
00577     assert(patternID != ID_FAIL);
00578     DBGLOG(DBG, "patternID=" << patternID);
00579     const OrdinaryAtom& pattern = ctx->registry()->ogatoms.getByAddress(patternID.address);
00580 
00581     // rewrite atoms of form aux(p1,p2,p3,...) to aux(X1,X2,X3,...) and remember the variables used for the predicates
00582     std::map<ID, ID> translation;
00583     for (uint32_t par = 0; par < inputType.size(); ++par) {
00584         if (getInputType(par) == PREDICATE && prop.isIndependentOfPredicateParameterName(par)) {
00585             if (translation.find(pattern.tuple[par + 1]) == translation.end()) {
00586                 std::stringstream var;
00587                 var << "X" << (par + 1);
00588                 translation[pattern.tuple[par + 1]] = ctx->registry()->storeVariableTerm(var.str());
00589                 DBGLOG(DBG, "Mapping " << pattern.tuple[par + 1] << " to " << translation[pattern.tuple[par + 1]]);
00590             }
00591         }
00592     }
00593 
00594     // translate the nogood
00595     Nogood translatedNG;
00596     BOOST_FOREACH (ID lID, ng) {
00597         const OrdinaryAtom& l = ctx->registry()->ogatoms.getByAddress(lID.address);
00598         if (lID != patternID) {
00599             OrdinaryAtom t = l;
00600             if (translation.find(l.tuple[0]) != translation.end()) {
00601                 t.tuple[0] = translation[l.tuple[0]];
00602             }
00603             if (t.tuple[0].isVariableTerm()) {
00604                 t.kind &= (ID::ALL_ONES ^ ID::SUBKIND_MASK);
00605                 t.kind |= ID::SUBKIND_ATOM_ORDINARYN;
00606             }
00607             ID id = NogoodContainer::createLiteral(ctx->registry()->storeOrdinaryAtom(t).address, !lID.isNaf(), !t.tuple[0].isVariableTerm());
00608             DBGLOG(DBG, "Adding translated literal " << id << " to nogood");
00609             translatedNG.insert(id);
00610         }
00611         else {
00612             bool ground = true;
00613             OrdinaryAtom t = l;
00614             for (uint32_t i = 1; i <= inputType.size(); ++i) {
00615                 if (getInputType(i - 1) == PREDICATE) {
00616                     if (translation.find(pattern.tuple[i]) != translation.end()) {
00617                         t.tuple[i] = translation[pattern.tuple[i]];
00618                     }
00619                     if (t.tuple[i].isVariableTerm()) {
00620                         ground = false;
00621                     }
00622                 }
00623                 else {
00624                     t.tuple[i] = pattern.tuple[i];
00625                 }
00626             }
00627             if (!ground) {
00628                 t.kind &= (ID::ALL_ONES ^ ID::SUBKIND_MASK);
00629                 t.kind |= ID::SUBKIND_ATOM_ORDINARYN;
00630             }
00631             ID id = NogoodContainer::createLiteral(ctx->registry()->storeOrdinaryAtom(t).address, !lID.isNaf(), ground);
00632             DBGLOG(DBG, "Adding translated literal " << id << " to nogood");
00633             translatedNG.insert(id);
00634         }
00635     }
00636 
00637     // store the translated nogood
00638     DBGLOG(DBG, "Adding generalized nogood " << translatedNG.getStringRepresentation(ctx->registry()) << " (from " << ng.getStringRepresentation(ctx->registry()) << ")");
00639     nogoods->addNogood(translatedNG);
00640 }
00641 
00642 
00643 PluginAtom::InputType
00644 PluginAtom::getInputType(const unsigned index) const
00645 {
00646     if (index >= inputType.size()) {
00647         assert(inputType.back() == TUPLE);
00648         return inputType.back();
00649     }
00650 
00651     return inputType[index];
00652 }
00653 
00654 
00655 void PluginAtom::setRegistry(RegistryPtr reg)
00656 {
00657     // i think we really don't want to change registry during the lifetime,
00658     // it would invalidate the cache and more bad things would happen
00659     assert(registry == 0);
00660     assert(reg != 0);
00661     registry = reg;
00662     predicateID = registry->terms.getIDByString(predicate);
00663     if( predicateID == ID_FAIL ) {
00664         Term t(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, predicate);
00665         predicateID = registry->terms.storeAndGetID(t);
00666     }
00667     replacements = PredicateMaskPtr(new PredicateMask());
00668     replacements->setRegistry(reg);
00669     replacements->addPredicate(reg->getAuxiliaryConstantSymbol('r', predicateID));
00670     replacements->updateMask();
00671     assert(predicateID != ID_FAIL);
00672 }
00673 
00674 
00675 std::vector<PluginAtomPtr>
00676 PluginInterface::createAtoms(ProgramCtx& ctx) const
00677 {
00678     return std::vector<PluginAtomPtr>();
00679 }
00680 
00681 
00682 // call printUsage for each loaded plugin
00683 void PluginInterface::printUsage(std::ostream& o) const
00684 {
00685     // don't fail if no usage message has been defined, simply do not give one
00686 }
00687 
00688 
00689 // call processOptions for each loaded plugin
00690 // (this is supposed to remove "recognized" options from pluginOptions)
00691 void PluginInterface::processOptions(std::list<const char*>& pluginOptions, ProgramCtx& ctx)
00692 {
00693     // don't fail if no option processing has been defined, simply do not process
00694 }
00695 
00696 
00697 PluginConverterPtr
00698 PluginInterface::createConverter(ProgramCtx&)
00699 {
00700     return PluginConverterPtr();
00701 }
00702 
00703 
00704 std::vector<PluginConverterPtr>
00705 PluginInterface::createConverters(ProgramCtx& ctx)
00706 {
00707     // per default return the single converter created by createConverter
00708     std::vector<PluginConverterPtr> ret;
00709     PluginConverterPtr pc = this->createConverter(ctx);
00710     if( pc )
00711         ret.push_back(pc);
00712     return ret;
00713 }
00714 
00715 
00716 std::vector<HexParserModulePtr>
00717 PluginInterface::createParserModules(ProgramCtx&)
00718 {
00719     return std::vector<HexParserModulePtr>();
00720 }
00721 
00722 
00723 HexParserPtr PluginInterface::createParser(ProgramCtx&)
00724 {
00725     return HexParserPtr();
00726 }
00727 
00728 
00729 PluginRewriterPtr PluginInterface::createRewriter(ProgramCtx&)
00730 {
00731     return PluginRewriterPtr();
00732 }
00733 
00734 
00735 PluginOptimizerPtr PluginInterface::createOptimizer(ProgramCtx&)
00736 {
00737     return PluginOptimizerPtr();
00738 }
00739 
00740 
00741 DLVHEX_NAMESPACE_END
00742 
00743 
00744 // vim:expandtab:ts=4:sw=4:
00745 // mode: C++
00746 // End:
00747