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 00035 #ifdef HAVE_CONFIG_H 00036 #include "config.h" 00037 #endif // HAVE_CONFIG_H 00038 00039 #include "dlvhex2/PredicateMask.h" 00040 #include "dlvhex2/Interpretation.h" 00041 #include "dlvhex2/Logger.h" 00042 #include "dlvhex2/Printhelpers.h" 00043 #include "dlvhex2/Registry.h" 00044 #include "dlvhex2/Printer.h" 00045 #include "dlvhex2/OrdinaryAtomTable.h" 00046 #include "dlvhex2/PluginInterface.h" 00047 #include "dlvhex2/ProgramCtx.h" 00048 00049 #include <boost/foreach.hpp> 00050 00051 DLVHEX_NAMESPACE_BEGIN 00052 00053 PredicateMask::PredicateMask(): 00054 maski(), 00055 knownAddresses(0) 00056 { 00057 } 00058 00059 00060 PredicateMask::PredicateMask(const PredicateMask& other): 00061 predicates(other.predicates), 00062 maski(other.maski), 00063 knownAddresses(other.knownAddresses), 00064 updateMutex() // must not copy mutex! 00065 { 00066 if( !!other.maski ) 00067 LOG(WARNING,"copied PredicateMask with non-NULL mask!"); 00068 } 00069 00070 00071 PredicateMask& 00072 PredicateMask::operator=(const PredicateMask& other) 00073 { 00074 predicates = other.predicates; 00075 maski = other.maski; 00076 knownAddresses = other.knownAddresses; 00077 // must not copy mutex! 00078 if( !!other.maski ) 00079 LOG(WARNING,"assigned PredicateMask with non-NULL mask!"); 00080 return *this; 00081 } 00082 00083 00084 PredicateMask::~PredicateMask() 00085 { 00086 } 00087 00088 00089 void PredicateMask::setRegistry(RegistryPtr reg) 00090 { 00091 assert((!maski || maski->getRegistry() == reg) && "PredicateMask cannot change registry!"); 00092 if( !maski ) { 00093 maski.reset(new Interpretation(reg)); 00094 } 00095 } 00096 00097 00098 void PredicateMask::addPredicate(ID pred) 00099 { 00100 boost::mutex::scoped_lock lock(updateMutex); 00101 00102 DBGLOG_VSCOPE(DBG,"PM::aP",this,false); 00103 DBGLOG(DBG,"adding predicate " << pred << ", knownAddresses was " << knownAddresses); 00104 assert(pred.isTerm() && pred.isConstantTerm() && "predicate masks can only be done on constant terms"); 00105 predicates.insert(pred.address); 00106 knownAddresses = 0; // scan the whole address space again 00107 } 00108 00109 00110 void PredicateMask::updateMask() 00111 { 00112 //DBGLOG_VSCOPE(DBG,"PM::uM",this,false); 00113 //DBGLOG(DBG,"= PredicateMask::updateMask for predicates " << 00114 // printset(predicates)); 00115 00116 assert(!!maski); 00117 RegistryPtr reg = maski->getRegistry(); 00118 Interpretation::Storage& bits = maski->getStorage(); 00119 00120 unsigned maxaddr = 0; 00121 00122 OrdinaryAtomTable::AddressIterator it_begin; 00123 { 00124 // get one state of it_end, encoded in maxaddr 00125 // (we must not use it_end, as it is a generic object but denotes 00126 // different endpoints if ogatoms changes during this method) 00127 OrdinaryAtomTable::AddressIterator it_end; 00128 boost::tie(it_begin, it_end) = reg->ogatoms.getAllByAddress(); 00129 maxaddr = it_end - it_begin; 00130 } 00131 00132 boost::mutex::scoped_lock lock(updateMutex); 00133 00134 // check if we have unknown atoms 00135 //DBGLOG(DBG,"already inspected ogatoms with address < " << knownAddresses << 00136 // ", iterator range has size " << maxaddr); 00137 if( maxaddr == knownAddresses ) 00138 return; 00139 00140 // only log real activity 00141 DBGLOG_VSCOPE(DBG,"PM::uM(do)",this,false); 00142 DBGLOG(DBG,"= PredicateMask::updateMask (need to update) for predicates " << 00143 printset(predicates)); 00144 00145 // if not equal, it must be larger -> we must inspect 00146 assert(maxaddr > knownAddresses); 00147 00148 // advance iterator to first ogatom unknown to predicateInputMask 00149 OrdinaryAtomTable::AddressIterator it = it_begin; 00150 it += knownAddresses; 00151 00152 unsigned missingBits = maxaddr - knownAddresses; 00153 DBGLOG(DBG,"need to inspect " << missingBits << " missing bits"); 00154 00155 // check all new ogatoms till the end 00156 #ifndef NDEBUG 00157 { 00158 std::stringstream s; 00159 s << "relevant predicate constants are "; 00160 RawPrinter printer(s, reg); 00161 BOOST_FOREACH(IDAddress addr, predicates) { 00162 printer.print(ID(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, addr)); 00163 s << ", "; 00164 } 00165 DBGLOG(DBG,s.str()); 00166 } 00167 #endif 00168 assert(knownAddresses == (it - it_begin)); 00169 for(;missingBits != 0; it++, missingBits--) { 00170 if (!reg->ogatoms.getIDByAddress(knownAddresses).isHiddenAtom()) { 00171 assert(it != reg->ogatoms.getAllByAddress().second); 00172 const OrdinaryAtom& oatom = *it; 00173 //DBGLOG(DBG,"checking " << oatom.tuple.front()); 00174 IDAddress addr = oatom.tuple.front().address; 00175 if( predicates.find(addr) != predicates.end() ) { 00176 bits.set(it - it_begin); 00177 } 00178 } 00179 knownAddresses++; 00180 } 00181 // knownAddresses += missingBits; 00182 DBGLOG(DBG,"updateMask created new set of relevant ogatoms: " << *maski << " and knownAddresses is " << knownAddresses); 00183 } 00184 00185 00186 ExternalAtomMask::ExternalAtomMask() : PredicateMask(), ctx(0), eatom(0) 00187 { 00188 } 00189 00190 00191 ExternalAtomMask::~ExternalAtomMask() 00192 { 00193 } 00194 00195 00196 // assumption: this is called once only 00197 // implicit assumption (as groundidb is const& and not stored in the class): groundidb does not change 00198 void ExternalAtomMask::setEAtom(const ProgramCtx& ctx, const ExternalAtom& eatom, const std::vector<ID>& groundidb) 00199 { 00200 assert(this->ctx == 0 && this->eatom == 0 && !this->outputAtoms && "we should never set the eatom twice!"); 00201 00202 LOG_SCOPE(DBG,"sEA",false); 00203 LOG(DBG," = ExternalAtomMask::setEAtom for " << eatom << " and " << groundidb.size() << " rules in groundidb"); 00204 00205 this->eatom = &eatom; 00206 this->ctx = &ctx; 00207 RegistryPtr reg = eatom.pluginAtom->getRegistry(); 00208 setRegistry(reg); 00209 outputAtoms.reset(new Interpretation(reg)); 00210 auxInputMask.reset(new Interpretation(reg)); 00211 00212 // positive and negative replacement predicates for this eatom 00213 ID posreplacement = reg->getAuxiliaryConstantSymbol('r', eatom.predicate); 00214 ID negreplacement = reg->getAuxiliaryConstantSymbol('n', eatom.predicate); 00215 00216 // replacement tuple cache 00217 preparedTuple.push_back(posreplacement); 00218 if( eatom.auxInputPredicate != ID_FAIL && 00219 ctx.config.getOption("IncludeAuxInputInAuxiliaries") ) { 00220 preparedTuple.push_back(eatom.auxInputPredicate); 00221 } 00222 00223 // 00224 // inputs 00225 // 00226 00227 // aux input predicate for this eatom 00228 if (eatom.auxInputPredicate != ID_FAIL) { 00229 DBGLOG(DBG, "Adding auxiliary input predicate " << printToString<RawPrinter>(eatom.auxInputPredicate,reg)); 00230 addPredicate(eatom.auxInputPredicate); 00231 } 00232 00233 // predicates of predicate inputs for this eatom 00234 int i = 0; 00235 BOOST_FOREACH (ID p, eatom.inputs) { 00236 preparedTuple.push_back(p); 00237 if (eatom.pluginAtom->getInputType(i) == PluginAtom::PREDICATE) { 00238 DBGLOG(DBG, "Adding input predicate " << printToString<RawPrinter>(p,reg)); 00239 addPredicate(p); 00240 } 00241 i++; 00242 } 00243 00244 // 00245 // outputs 00246 // 00247 preparedTuple.insert(preparedTuple.end(), eatom.tuple.begin(), eatom.tuple.end()); 00248 workTuple.assign(preparedTuple.begin(), preparedTuple.end()); 00249 DBGLOG(DBG,"preparedTuple is <" << printManyToString<RawPrinter>(preparedTuple, ", ", reg) << ">"); 00250 00251 // find all output atoms (replacement atoms for positive or negative external atoms) 00252 // which possibly belong to this external atom 00253 BOOST_FOREACH (ID rId, groundidb) { 00254 // TODO we could mark PROPERTY_RULE_EXTATOMS for external replacement atoms coming from gringo, then we can rule out all rules without extatoms here by a bit check, perhaps it will not pay off however 00255 00256 // I think we cannot use a PredicateMask to get these atoms (by simply matching 00257 // posreplacement and negreplacement) because then we get everything in the registry, 00258 // not only those in groundidb, and if we need to count them to ensure we have every 00259 // bit that is relevant (as done for verification of external atoms) this will not 00260 // work anymore. 00261 const Rule& rule = reg->rules.getByID(rId); 00262 BOOST_FOREACH (ID h, rule.head) { 00263 if (h.isExternalAuxiliary()) { 00264 const OrdinaryAtom& atom = reg->ogatoms.getByID(h); 00265 if (atom.tuple[0] == posreplacement || atom.tuple[0] == negreplacement) { 00266 outputAtoms->setFact(h.address); 00267 } 00268 } 00269 } 00270 BOOST_FOREACH (ID b, rule.body) { 00271 if (b.isExternalAuxiliary()) { 00272 const OrdinaryAtom& atom = reg->ogatoms.getByID(b); 00273 if (atom.tuple[0] == posreplacement || atom.tuple[0] == negreplacement) { 00274 outputAtoms->setFact(b.address); 00275 } 00276 } 00277 } 00278 } 00279 DBGLOG(DBG, "Watching " << outputAtoms->getStorage().count() << " output atoms: " << *outputAtoms); 00280 addOutputAtoms(outputAtoms); 00281 } 00282 00283 00284 void ExternalAtomMask::addOutputAtoms(InterpretationConstPtr intr) 00285 { 00286 00287 assert(!!eatom); 00288 RegistryPtr reg = ctx->registry(); 00289 ID posreplacement = reg->getAuxiliaryConstantSymbol('r', eatom->predicate); 00290 ID negreplacement = reg->getAuxiliaryConstantSymbol('n', eatom->predicate); 00291 00292 bm::bvector<>::enumerator en = intr->getStorage().first(); 00293 bm::bvector<>::enumerator en_end = intr->getStorage().end(); 00294 while (en < en_end) { 00295 const IDAddress atom = *en; 00296 ID id = reg->ogatoms.getIDByAddress(atom); 00297 if (id.isExternalAuxiliary()) { 00298 const OrdinaryAtom& oatom = ctx->registry()->ogatoms.getByAddress(atom); 00299 if (oatom.tuple[0] == posreplacement || oatom.tuple[0] == negreplacement) { 00300 if (matchOutputAtom(oatom.tuple)) { 00301 DBGLOG(DBG, "Output atom " << oatom.text << " matches the external atom"); 00302 maski->setFact(atom); 00303 } 00304 else { 00305 DBGLOG(DBG, "Output atom " << oatom.text << " does not match the external atom"); 00306 } 00307 } 00308 } 00309 en++; 00310 } 00311 } 00312 00313 00314 bool ExternalAtomMask::matchOutputAtom(const Tuple& togatom) 00315 { 00316 assert(eatom); 00317 00318 //RegistryPtr reg = maski->getRegistry(); 00319 //DBGLOG(WARNING,"workTuple=" << printManyToString<RawPrinter>(workTuple, ", ", reg) << " togatom=" << printManyToString<RawPrinter>(togatom, ", ", reg)); 00320 00321 // store togatom into workTuple if possible, otherwise bailout 00322 // then restore workTuple 00323 // this must be checked and cannot be asserted because multiple external atoms with the same predicate can have inputs of different sizes 00324 if (workTuple.size() != togatom.size()) return false; 00325 // assert(workTuple.size() == togatom.size()); 00326 assert(workTuple == preparedTuple); 00327 bool ret = true; 00328 for(unsigned idx = 1; idx < togatom.size(); ++idx) { 00329 ID query = togatom[idx]; 00330 ID pattern = workTuple[idx]; 00331 if( pattern.isVariableTerm() ) { 00332 for(unsigned i = idx; i < togatom.size(); ++i) { 00333 if( workTuple[i] == pattern ) 00334 workTuple[i] = query; 00335 } 00336 } 00337 else { 00338 assert(pattern.isConstantTerm() || pattern.isIntegerTerm()); 00339 if( pattern != query ) { 00340 ret = false; 00341 break; 00342 } 00343 } 00344 } 00345 00346 // restore workTuple 00347 workTuple.assign(preparedTuple.begin(), preparedTuple.end()); 00348 return ret; 00349 00350 #if 0 00351 #ifndef NDEBUG 00352 std::stringstream ss; 00353 ss << "Comparing togatom tuple ("; 00354 for (int i = 0; i < togatom.size(); ++i) { 00355 ss << (i > 0 ? ", " : ""); 00356 if (togatom[i].isIntegerTerm()) { 00357 ss << togatom[i].address; 00358 } 00359 else { 00360 ss << eatom->pluginAtom->getRegistry()->terms.getByID(togatom[i]).symbol; 00361 } 00362 } 00363 ss << ") to external atom " << eatom->pluginAtom->getRegistry()->terms.getByID(eatom->predicate).symbol << " (input: "; 00364 for (int i = 0; i < eatom->inputs.size(); ++i) { 00365 ss << (i > 0 ? ", " : ""); 00366 if (eatom->inputs[i].isIntegerTerm()) { 00367 ss << eatom->inputs[i].address; 00368 } 00369 else { 00370 ss << eatom->pluginAtom->getRegistry()->terms.getByID(eatom->inputs[i]).symbol; 00371 } 00372 } 00373 ss << "; output: "; 00374 for (int i = 0; i < eatom->tuple.size(); ++i) { 00375 ss << (i > 0 ? ", " : ""); 00376 if (eatom->tuple[i].isIntegerTerm()) { 00377 ss << eatom->tuple[i].address; 00378 } 00379 else { 00380 ss << eatom->pluginAtom->getRegistry()->terms.getByID(eatom->tuple[i]).symbol; 00381 } 00382 } 00383 ss << ")"; 00384 DBGLOG(DBG, ss.str()); 00385 #endif 00386 #endif 00387 00388 #if 0 00389 // check predicate and constant input 00390 int aux = 0; 00391 if (ctx->config.getOption("IncludeAuxInputInAuxiliaries") && eatom->auxInputPredicate != ID_FAIL) { 00392 if (togatom[1] != eatom->auxInputPredicate) return false; 00393 aux = 1; 00394 } 00395 for (unsigned p = 0; p < eatom->inputs.size(); ++p) { 00396 if (eatom->pluginAtom->getInputType(p) == PluginAtom::PREDICATE || 00397 (eatom->pluginAtom->getInputType(p) == PluginAtom::CONSTANT && !eatom->inputs[p].isVariableTerm())) { 00398 if (togatom[p + aux + 1] != eatom->inputs[p]) { 00399 //DBGLOG(DBG, "Predicate or constant input mismatch"); 00400 // do not assign variable already here, because we later check against input tuples, this is a redundant fast check to eliminate mismatches in constants and predicates 00401 return false; 00402 } 00403 } 00404 } 00405 00406 // remember variable binding 00407 // using map here is not too evil as we have a maximum of eatom->inputs.size() elements in map -> very fast, probably faster than hashtable 00408 typedef std::map<ID, ID> VBMap; 00409 VBMap varBinding; 00410 00411 // check auxiliary input 00412 bool inputmatch = false; 00413 if (eatom->auxInputPredicate == ID_FAIL) { 00414 inputmatch = true; 00415 } 00416 else { 00417 bm::bvector<>::enumerator en = auxInputMask->getStorage().first(); 00418 bm::bvector<>::enumerator en_end = auxInputMask->getStorage().end(); 00419 for(; en < en_end; ++en) { 00420 const OrdinaryAtom& tinp = reg->ogatoms.getByAddress(*en); 00421 // check if tinp corresponds to togatom 00422 bool match = true; 00423 for (unsigned i = 1; i < tinp.tuple.size(); ++i) { 00424 for(std::list<unsigned>::const_iterator it = eatom->auxInputMapping[i-1].begin(); 00425 it != eatom->auxInputMapping[i-1].end(); ++it) { 00426 const unsigned& pos = *it; 00427 if (togatom[aux + 1 + pos] != tinp.tuple[i]) { 00428 match = false; 00429 break; 00430 } 00431 // remember matched variables 00432 varBinding[eatom->inputs[pos]] = tinp.tuple[i]; 00433 } 00434 if (!match) break; 00435 } 00436 if (match) { 00437 inputmatch = true; 00438 break; 00439 } 00440 } 00441 if (!inputmatch) { 00442 //DBGLOG(DBG, "Auxiliary input mismatch"); 00443 } 00444 } 00445 if (!inputmatch) return false; 00446 00447 // check output tuple 00448 for (unsigned o = 0; o < eatom->tuple.size(); ++o) { 00449 if (eatom->tuple[o].isVariableTerm()) { 00450 VBMap::iterator vbit = varBinding.find(eatom->tuple[o]); 00451 if (vbit == varBinding.end()) { 00452 vbit->second = togatom[aux + 1 + eatom->inputs.size() + o]; 00453 } 00454 else { 00455 if (vbit->second != togatom[aux + 1 + eatom->inputs.size() + o]) { 00456 return false; 00457 } 00458 } 00459 } 00460 else if (eatom->tuple[o].isConstantTerm() || eatom->tuple[o].isIntegerTerm()) { 00461 if (togatom[aux + 1 + eatom->inputs.size() + o] != eatom->tuple[o]) { 00462 return false; 00463 } 00464 } 00465 else { 00466 assert(false); 00467 } 00468 } 00469 return true; 00470 #endif 00471 } 00472 00473 00474 // this method ensures that the mask captures: 00475 // * predicate input of the external atom 00476 // * auxiliary input to the external atom 00477 // * all ground output atoms (replacements) of the external atom 00478 // this is the set of all ogatoms which use the (positive or negative) auxiliary of this external atom 00479 // and where the input list matches 00480 // 00481 // the update strategy is as follows: 00482 // 1. update as usual 00483 // 2. if an auxiliary input atom was added, consider all ogatoms over the positive or negative replacement as new 00484 // 3. for all new ogatoms over the positive or negative replacement, check if the input list matches (if not: remove the atom from the mask) 00485 void ExternalAtomMask::updateMask() 00486 { 00487 assert(eatom); 00488 DBGLOG(DBG, "ExternalAtomMask::updateMask"); 00489 00490 // remember what changed 00491 // FIXME we can perhaps make this change computation faster and reduce reallocations using some nice bitmagic operators 00492 InterpretationPtr change = InterpretationPtr(new Interpretation(eatom->pluginAtom->getRegistry())); 00493 change->getStorage() |= maski->getStorage(); 00494 PredicateMask::updateMask(); 00495 change->getStorage() ^= maski->getStorage(); 00496 boost::mutex::scoped_lock lock(updateMutex); 00497 00498 if (change->getStorage().count() == 0) return; 00499 00500 // check if an atom over the auxiliary input predicate was added 00501 // (this is not done as a PredicateMask because it alread gets the delta in change) 00502 bool auxAdded = false; 00503 bm::bvector<>::enumerator en = change->getStorage().first(); 00504 bm::bvector<>::enumerator en_end = change->getStorage().end(); 00505 while (en < en_end) { 00506 const OrdinaryAtom& oatom = eatom->pluginAtom->getRegistry()->ogatoms.getByAddress(*en); 00507 if (oatom.tuple[0] == eatom->auxInputPredicate) { 00508 auxInputMask->setFact(*en); 00509 auxAdded = true; 00510 } 00511 en++; 00512 } 00513 00514 // if an auxiliary input atom was added, we have to recheck all output atoms 00515 if (auxAdded) { 00516 DBGLOG(DBG, "Auxiliary input changed"); 00517 // recheck all output atoms 00518 bm::bvector<>::enumerator en = outputAtoms->getStorage().first(); 00519 bm::bvector<>::enumerator en_end = outputAtoms->getStorage().end(); 00520 while (en < en_end) { 00521 const IDAddress outputAtom = *en; 00522 const OrdinaryAtom& oatom = eatom->pluginAtom->getRegistry()->ogatoms.getByAddress(outputAtom); 00523 if (matchOutputAtom(oatom.tuple)) { 00524 DBGLOG(DBG, "Output atom " << oatom.text << " matches the external atom"); 00525 maski->setFact(outputAtom); 00526 } 00527 else { 00528 DBGLOG(DBG, "Output atom " << oatom.text << " does not match the external atom"); 00529 } 00530 en++; 00531 } 00532 } 00533 } 00534 00535 00536 const InterpretationConstPtr ExternalAtomMask::getAuxInputMask() const 00537 { 00538 assert (!!auxInputMask && "auxInputMask not set"); 00539 return auxInputMask; 00540 } 00541 00542 00543 DLVHEX_NAMESPACE_END 00544 00545 00546 // vim:expandtab:ts=4:sw=4: 00547 // mode: C++ 00548 // End: