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