dlvhex  2.5.0
src/ComfortPluginInterface.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 #include "dlvhex2/ComfortPluginInterface.h"
00035 WARNING("TODO how to implement strong negation (StrongNegationPlugin) with ComfortPluginInterface?")
00036 
00037 #ifdef HAVE_CONFIG_H
00038 #  include "config.h"
00039 #endif
00040 
00041 #include "dlvhex2/Benchmarking.h"
00042 #include "dlvhex2/Printer.h"
00043 #include "dlvhex2/ProgramCtx.h"
00044 #include "dlvhex2/Registry.h"
00045 
00046 #include <sstream>
00047 
00048 DLVHEX_NAMESPACE_BEGIN
00049 
00050 namespace
00051 {
00052     typedef std::set<ComfortAtom> IntBase;
00053 }
00054 
00055 
00056 std::ostream& ComfortTerm::print(std::ostream& o) const
00057 {
00058     if( isInteger() )
00059         return o << intval;
00060     else
00061         return o << strval;
00062 }
00063 
00064 
00065 // see OrdinaryAtom::unifiesWith
00066 bool ComfortAtom::unifiesWith(
00067 const ComfortAtom& a) const
00068 {
00069     if( tuple.size() != a.tuple.size() )
00070         return false;
00071 
00072     DBGLOG_SCOPE(DBG,"CA::uw",false);
00073     DBGLOG(DBG,"= ComfortAtom::unifiesWith");
00074 
00075     // unify from left to right
00076     ComfortTuple result1(this->tuple);
00077     ComfortTuple result2(a.tuple);
00078     // if both tuples have a variable, assign result1 variable to result2 for all occurences to the end
00079     // if one tuple has constant, assign this constant into the other tuple for all occurences to the end
00080     ComfortTuple::iterator it1, it2;
00081     DBGLOG(DBG,"starting with result1 tuple " << printvector(result1));
00082     DBGLOG(DBG,"starting with result2 tuple " << printvector(result2));
00083     for(it1 = result1.begin(), it2 = result2.begin();
00084         it1 != result1.end();
00085     ++it1, ++it2) {
00086         DBGLOG(DBG,"at position " << static_cast<unsigned>(it1 - result1.begin()) <<
00087             ": checking " << *it1 << " vs " << *it2);
00088         if( *it1 != *it2 ) {
00089             // different terms
00090             if( it1->isVariable() ) {
00091                 // it1 is variable
00092                 if( it2->isVariable() ) {
00093                     // it2 is variable
00094 
00095                     // assign *it1 variable to all occurances of *it2 in result2
00096                     ComfortTuple::iterator it3(it2); it3++;
00097                     for(;it3 != result2.end(); ++it3) {
00098                         if( *it3 == *it2 )
00099                             *it3 = *it1;
00100                     }
00101                 }
00102                 else {
00103                     // it2 is nonvariable
00104 
00105                     // assign *it2 nonvariable to all occurances of *it1 in result1
00106                     ComfortTuple::iterator it3(it1); it3++;
00107                     for(;it3 != result1.end(); ++it3) {
00108                         if( *it3 == *it1 )
00109                             *it3 = *it2;
00110                     }
00111                 }
00112             }
00113             else {
00114                 // it1 is nonvariable
00115                 if( it2->isVariable() ) {
00116                     // it2 is variable
00117 
00118                     // assign *it1 nonvariable to all occurances of *it2 in result2
00119                     ComfortTuple::iterator it3(it2); it3++;
00120                     for(;it3 != result2.end(); ++it3) {
00121                         if( *it3 == *it2 )
00122                             *it3 = *it1;
00123                     }
00124                 }
00125                 else {
00126                     // it2 is nonvariable
00127                     return false;
00128                 }
00129             }
00130             DBGLOG(DBG,"after propagation of difference (look only after current position!):");
00131             DBGLOG(DBG,"result1 tuple " << printvector(result1));
00132             DBGLOG(DBG,"result2 tuple " << printvector(result2));
00133         }
00134     }
00135     return true;
00136 }
00137 
00138 
00139 void ComfortAtom::calculateStrVal() const
00140 {
00141     ComfortTuple::const_iterator it = tuple.begin();
00142     assert(it != tuple.end());
00143 
00144     std::ostringstream os;
00145     os << *it;
00146     it++;
00147     if( it != tuple.end() ) {
00148         os << "(" << *it;
00149         it++;
00150         while(it != tuple.end()) {
00151             os << "," << *it;
00152             it++;
00153         }
00154         os << ")";
00155     }
00156     strval = os.str();
00157 }
00158 
00159 
00160 std::ostream& ComfortAtom::print(std::ostream& o) const
00161 {
00162     return o << toString();
00163 }
00164 
00165 
00166 // insert one atom
00167 void ComfortInterpretation::insert(const ComfortAtom& atm)
00168 {
00169     IntBase::insert(atm);
00170 }
00171 
00172 
00173 // insert all atoms from other interpretation
00174 void ComfortInterpretation::insert(const ComfortInterpretation& other)
00175 {
00176     IntBase::insert(other.begin(), other.end());
00177 }
00178 
00179 
00180 namespace
00181 {
00182     struct MatchPred
00183     {
00184         const std::set<ComfortTerm>& predicates;
00185         MatchPred(const std::set<ComfortTerm>& predicates):
00186         predicates(predicates) {}
00187         bool operator()(const ComfortAtom& atm) const
00188         {
00189             if( atm.tuple.empty() )
00190                 throw std::runtime_error("MatchPred: atom tuple must not be empty!");
00191             // remove if found
00192             return predicates.find(atm.tuple.front()) != predicates.end();
00193         }
00194     };
00195 }
00196 
00197 
00198 // remove atoms whose predicate matches a string in the given set
00199 void ComfortInterpretation::remove(const std::set<std::string>& predicates)
00200 {
00201     std::set<ComfortTerm> pred_constant_terms;
00202     BOOST_FOREACH(const std::string& pred, predicates) {
00203         pred_constant_terms.insert(ComfortTerm::createConstant(pred));
00204     }
00205 
00206     IntBase::iterator it = IntBase::begin();
00207     do {
00208         assert( !it->tuple.empty() && "tuple of ComfortAtom must contain at least a predicate");
00209         if( pred_constant_terms.find(it->tuple[0]) != pred_constant_terms.end() ) {
00210             // remove this one
00211             IntBase::iterator prev = it;
00212             IntBase::erase(prev);
00213             ++it;
00214         }
00215         else {
00216             ++it;
00217         }
00218     }
00219     while( it != IntBase::end() );
00220 }
00221 
00222 
00223 // remove atoms whose predicate does not match any string in the given set
00224 void ComfortInterpretation::keep(const std::set<std::string>& predicates)
00225 {
00226     std::set<ComfortTerm> pred_constant_terms;
00227     BOOST_FOREACH(const std::string& pred, predicates) {
00228         pred_constant_terms.insert(ComfortTerm::createConstant(pred));
00229     }
00230 
00231     IntBase::iterator it = IntBase::begin();
00232     do {
00233         assert( !it->tuple.empty() && "tuple of ComfortAtom must contain at least a predicate");
00234         if( pred_constant_terms.find(it->tuple[0]) == pred_constant_terms.end() ) {
00235             // remove this one
00236             IntBase::iterator prev = it;
00237             IntBase::erase(prev);
00238             ++it;
00239         }
00240         else {
00241             ++it;
00242         }
00243     }
00244     while( it != IntBase::end() );
00245 }
00246 
00247 
00248 // copy all atoms that match the specified predicate into destination interpretation
00249 void ComfortInterpretation::matchPredicate(const std::string& predicate, ComfortInterpretation& destination) const
00250 {
00251     ComfortTerm pred = ComfortTerm::createConstant(predicate);
00252 
00253     for(IntBase::iterator it = IntBase::begin();
00254     it != IntBase::end(); ++it) {
00255         assert( !it->tuple.empty() && "tuple of ComfortAtom must contain at least a predicate");
00256         if( pred == it->tuple[0] ) {
00257             // add to destination
00258             destination.insert(*it);
00259         }
00260     }
00261 }
00262 
00263 
00264 // copy all atoms that unify with the specified predicate into destination interpretation
00265 void ComfortInterpretation::matchAtom(const ComfortAtom& atom, ComfortInterpretation& destination) const
00266 {
00267     for(IntBase::iterator it = IntBase::begin();
00268     it != IntBase::end(); ++it) {
00269         assert( !it->tuple.empty() && "tuple of ComfortAtom must contain at least a predicate");
00270         if( it->unifiesWith(atom) ) {
00271             // add to destination
00272             destination.insert(*it);
00273         }
00274     }
00275 }
00276 
00277 
00278 // return set difference *this \ subtractThis
00279 ComfortInterpretation ComfortInterpretation::difference(const ComfortInterpretation& subtractThis) const
00280 {
00281     ComfortInterpretation ret;
00282     std::insert_iterator<IntBase> inserter(ret, ret.begin());
00283     std::set_difference(
00284         IntBase::begin(), IntBase::end(),
00285         subtractThis.begin(), subtractThis.end(),
00286         inserter);
00287     return ret;
00288 }
00289 
00290 
00291 std::ostream& ComfortInterpretation::print(std::ostream& o) const
00292 {
00293     return o << printrange(*this, "{", "}", ",");
00294 }
00295 
00296 
00297 bool ComfortInterpretation::operator==(const ComfortInterpretation& c2) const
00298 {
00299     return this->difference(c2).size() == 0 && c2.difference(*this).size() == 0;
00300 }
00301 
00302 
00303 namespace
00304 {
00305     ComfortTerm convertTerm(RegistryPtr reg, ID tid) {
00306         assert(tid.isTerm());
00307         if( tid.isVariableTerm() ) {
00308             return ComfortTerm::createVariable(reg->getTermStringByID(tid));
00309         }
00310         else if( tid.isConstantTerm() ) {
00311             return ComfortTerm::createConstant(reg->getTermStringByID(tid));
00312         }
00313         else {
00314             assert(tid.isIntegerTerm());
00315             return ComfortTerm::createInteger(tid.address);
00316         }
00317     }
00318 
00319     void convertTuple(RegistryPtr reg, const Tuple& in, ComfortTuple& out) {
00320         assert(out.empty());
00321         BOOST_FOREACH(ID id, in) {
00322             out.push_back( convertTerm(reg, id) );
00323         }
00324     }
00325 
00326     ID convertTerm(RegistryPtr reg, const ComfortTerm& ct) {
00327         if( ct.isConstant() ) {
00328             Term t(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, ct.strval);
00329             WARNING("TODO check here(?) whether ct.strval has correct constant syntax")
00330                 return reg->storeTerm(t);
00331         }
00332         else if( ct.isInteger() ) {
00333             return ID::termFromInteger(ct.intval);
00334         }
00335         else {
00336             throw PluginError(
00337                 "plugins must not return variables in answer tuples "
00338                 "(got '" + ct.strval + "'");
00339         }
00340     }
00341 
00342     void convertTuple(RegistryPtr reg, const ComfortTuple& in, Tuple& out) {
00343         assert(out.empty());
00344         BOOST_FOREACH(const ComfortTerm& ct, in) {
00345             out.push_back( convertTerm(reg, ct) );
00346         }
00347     }
00348 }
00349 
00350 
00356 void ComfortPluginAtom::retrieve(const Query& query, Answer& answer)
00357 {
00358     DBGLOG_SCOPE(DBG,"CPA::r",false);
00359     DBGLOG(DBG,"= ComfortPluginAtom::retrieve()");
00360 
00361     RegistryPtr reg = getRegistry();
00362     assert(!!reg && "registry must be set for ComfortPluginAtom::retrieve(...)");
00363 
00364     // convert query
00365     ComfortQuery cq;
00366     convertTuple(reg, query.input, cq.input);
00367     convertTuple(reg, query.pattern, cq.pattern);
00368     for(Interpretation::Storage::enumerator it =
00369         query.interpretation->getStorage().first();
00370     it != query.interpretation->getStorage().end(); ++it) {
00371         ID ogid(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *it);
00372         const OrdinaryAtom& ogatom = reg->ogatoms.getByID(ogid);
00373         ComfortAtom cogatom;
00374         convertTuple(reg, ogatom.tuple, cogatom.tuple);
00375         DBGLOG(DBG,"converted ogatom " << ogatom <<
00376             " to " << printrange(cogatom.tuple));
00377         cq.interpretation.insert(cogatom);
00378     }
00379     DBGLOG(DBG,"query conversion result before calling comfort-retrieve:");
00380     DBGLOG(DBG,"  input=" << printrange(cq.input));
00381     DBGLOG(DBG,"  pattern=" << printrange(cq.pattern));
00382     DBGLOG(DBG,"  interpretation=" << printset(cq.interpretation));
00383 
00384     // call comfort retrieve method
00385     ComfortAnswer ca;
00386     retrieve(cq, ca);
00387 
00388     #ifndef NDEBUG
00389     if( ca.empty() ) {
00390         DBGLOG(DBG,"comfort-retrieve returned no answer tuples");
00391     }
00392     else {
00393         DBGLOG(DBG,"comfort-retrieve returned " << ca.size() << " answer tuples:");
00394         BOOST_FOREACH(const ComfortTuple& at, ca) {
00395             DBGLOG(DBG,"  " << printrange(at));
00396         }
00397     }
00398     #endif
00399 
00400     // convert back
00401     BOOST_FOREACH(const ComfortTuple& at, ca) {
00402         // avoid copying: first create, than directly convert into it
00403         answer.get().push_back(Tuple());
00404         convertTuple(reg, at, answer.get().back());
00405     }
00406 }
00407 
00408 
00409 DLVHEX_NAMESPACE_END
00410 
00411 // vim:expandtab:ts=4:sw=4:
00412 // mode: C++
00413 // End: