dlvhex
2.5.0
|
00001 /* dlvhex -- Answer-Set Programming with external interfaces. 00002 * Copyright (C) 2005, 2006, 2007 Roman Schindlauer 00003 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Thomas Krennwallner 00004 * Copyright (C) 2009, 2010 Peter Schüller 00005 * 00006 * This file is part of dlvhex. 00007 * 00008 * dlvhex is free software; you can redistribute it and/or modify it 00009 * under the terms of the GNU Lesser General Public License as 00010 * published by the Free Software Foundation; either version 2.1 of 00011 * the License, or (at your option) any later version. 00012 * 00013 * dlvhex is distributed in the hope that it will be useful, but 00014 * WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with dlvhex; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 00021 * 02110-1301 USA. 00022 */ 00023 00024 00036 #ifdef HAVE_CONFIG_H 00037 #include "config.h" 00038 #endif // HAVE_CONFIG_H 00039 00040 #if defined(HAVE_OWLCPP) 00041 #include "owlcpp/rdf/triple_store.hpp" 00042 #include "owlcpp/io/input.hpp" 00043 #include "owlcpp/io/catalog.hpp" 00044 #include "owlcpp/terms/node_tags_owl.hpp" 00045 #endif 00046 00047 00048 #include "dlvhex2/ExternalLearningHelper.h" 00049 #include "dlvhex2/ComfortPluginInterface.h" 00050 #include "dlvhex2/Term.h" 00051 #include "dlvhex2/Registry.h" 00052 #include "dlvhex2/ProgramCtx.h" 00053 #include "dlvhex2/Printer.h" 00054 00055 #include <boost/foreach.hpp> 00056 #include <boost/functional/hash.hpp> 00057 #include <boost/program_options.hpp> 00058 #include <boost/range.hpp> 00059 #include <boost/filesystem.hpp> 00060 #include <boost/lexical_cast.hpp> 00061 00062 #include <string> 00063 #include <sstream> 00064 #include <iostream> 00065 #include <map> 00066 00067 #include <cstdio> 00068 #include <cassert> 00069 00070 DLVHEX_NAMESPACE_BEGIN 00071 00072 class TestAAtom: 00073 public ComfortPluginAtom 00074 { 00075 public: 00076 TestAAtom(): 00077 ComfortPluginAtom("testA") 00078 { 00079 addInputPredicate(); 00080 setOutputArity(1); 00081 } 00082 00083 virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer) 00084 { 00085 ComfortTuple tu; 00086 if( query.interpretation.empty() ) 00087 { 00088 tu.push_back(ComfortTerm::createConstant("foo")); 00089 } 00090 else 00091 { 00092 tu.push_back(ComfortTerm::createConstant("bar")); 00093 } 00094 00095 answer.insert(tu); 00096 } 00097 }; 00098 00099 class TestBAtom: 00100 public ComfortPluginAtom 00101 { 00102 public: 00103 TestBAtom(): 00104 ComfortPluginAtom("testB") 00105 { 00106 addInputPredicate(); 00107 addInputPredicate(); 00108 setOutputArity(1); 00109 } 00110 00111 virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer) 00112 { 00113 if( query.interpretation.size() <= 1 ) 00114 { 00115 ComfortTuple tu; 00116 tu.push_back(ComfortTerm::createConstant("bar")); 00117 answer.insert(tu); 00118 } 00119 else if( query.interpretation.size() > 1 ) 00120 { 00121 ComfortTuple tu; 00122 tu.push_back(ComfortTerm::createConstant("foo")); 00123 answer.insert(tu); 00124 } 00125 } 00126 }; 00127 00128 class TestCAtom: 00129 public ComfortPluginAtom 00130 { 00131 public: 00132 TestCAtom(): 00133 ComfortPluginAtom("testC") 00134 { 00135 addInputPredicate(); 00136 setOutputArity(1); 00137 } 00138 00139 virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer) 00140 { 00141 assert(query.input.size() > 0); 00142 assert(query.input[0].isConstant()); 00143 00144 // was: std::string t = "-" + query.input[0].strval; 00145 std::string t = query.input[0].strval; 00146 00147 ComfortInterpretation proj; 00148 query.interpretation.matchPredicate(t, proj); 00149 00150 for(ComfortInterpretation::const_iterator it = proj.begin(); 00151 it != proj.end(); ++it) 00152 { 00153 const ComfortAtom& at = *it; 00154 ComfortTuple::const_iterator itt = at.tuple.begin(); 00155 assert(itt != at.tuple.end()); 00156 // skip predicate 00157 itt++; 00158 while( itt != at.tuple.end() ) 00159 { 00160 // add each constant of the atom as separate output tuple 00161 // so foo(a,b,c) will end up as three tuples [a], [b], and [c] 00162 ComfortTuple tu; 00163 tu.push_back(*itt); 00164 answer.insert(tu); 00165 itt++; 00166 } 00167 } 00168 } 00169 }; 00170 00171 // this is no comfortplugin atom as we don't need comfort here 00172 class TestZeroArityAtom: 00173 public PluginAtom 00174 { 00175 protected: 00176 bool succeed; 00177 public: 00178 TestZeroArityAtom(const std::string& name, bool succeed): 00179 PluginAtom(name, true), 00180 succeed(succeed) 00181 { 00182 // no inputs 00183 setOutputArity(0); 00184 } 00185 00186 virtual void retrieve(const Query&, Answer& answer) 00187 { 00188 if( succeed ) 00189 { 00190 // succeed by returning an empty tuple 00191 answer.get().push_back(Tuple()); 00192 } 00193 else 00194 { 00195 // fail by returning no tuple (but mark answer as set) 00196 answer.use(); 00197 } 00198 } 00199 }; 00200 00201 # if 0 00202 // comfort implementation 00203 class TestConcatAtom: 00204 public ComfortPluginAtom 00205 { 00206 public: 00207 TestConcatAtom(): 00208 ComfortPluginAtom("testConcat", true) // monotonic, and no predicate inputs anyway 00209 { 00210 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 00211 addInputTuple(); 00212 setOutputArity(1); 00213 } 00214 00215 virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer) 00216 { 00217 std::stringstream s; 00218 00219 BOOST_FOREACH(const ComfortTerm& t, query.input) 00220 { 00221 if( t.isInteger() ) 00222 s << t.intval; 00223 else if( t.isConstant() ) 00224 s << t.strval; 00225 else 00226 throw PluginError("encountered unknown term type!"); 00227 } 00228 00229 ComfortTuple tu; 00230 tu.push_back(ComfortTerm::createConstant(s.str())); 00231 answer.insert(tu); 00232 } 00233 }; 00234 #else 00235 // non-comfort implementation 00236 class TestConcatAtom: 00237 public PluginAtom 00238 { 00239 public: 00240 TestConcatAtom(): 00241 PluginAtom("testConcat", true) // monotonic, as there is no predicate input anyway 00242 { 00243 addInputTuple(); 00244 setOutputArity(1); 00245 00246 prop.functional = true; 00247 } 00248 00249 virtual void retrieve(const Query& query, Answer& answer) 00250 { 00251 std::stringstream s; 00252 00253 bool hasStrings = false; 00254 BOOST_FOREACH(ID tid, query.input) 00255 { 00256 assert(tid.isTerm()); 00257 if( tid.isIntegerTerm() ) 00258 s << tid.address; 00259 else if( tid.isConstantTerm() ) 00260 { 00261 const std::string& str = registry->getTermStringByID(tid); 00262 if( str[0] == '"' ) 00263 { 00264 hasStrings = true; 00265 s << str.substr(1,str.size()-2); 00266 } 00267 else 00268 { 00269 s << str; 00270 } 00271 } 00272 else 00273 throw PluginError("encountered unknown term type!"); 00274 } 00275 00276 // check if the result is an integer 00277 try 00278 { 00279 int intval = boost::lexical_cast<short>(s.str()); 00280 Tuple tu; 00281 tu.push_back(ID::termFromInteger(intval)); 00282 answer.get().push_back(tu); 00283 } 00284 catch(const boost::bad_lexical_cast &) 00285 { 00286 // not an integer 00287 Term resultterm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, s.str()); 00288 if( hasStrings ) 00289 resultterm.symbol = "\"" + resultterm.symbol + "\""; 00290 Tuple tu; 00291 tu.push_back(registry->storeTerm(resultterm)); 00292 // the next line would also work and be more efficient, but the above line tests more 00293 //tu.push_back(registry->storeConstOrVarTerm(resultterm)); 00294 answer.get().push_back(tu); 00295 } 00296 } 00297 }; 00298 #endif 00299 00300 class TestConcatAllAtom: 00301 public PluginAtom 00302 { 00303 public: 00304 TestConcatAllAtom(): 00305 PluginAtom("testConcatAll", false) // monotonic, as there is no predicate input anyway 00306 { 00307 addInputPredicate(); 00308 setOutputArity(1); 00309 00310 prop.functional = true; 00311 } 00312 00313 virtual void retrieve(const Query& query, Answer& answer) 00314 { 00315 std::stringstream s; 00316 00317 // RawPrinter printer(s, registry); 00318 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 00319 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 00320 while (en < en_end){ 00321 // printer.print(registry->ogatoms.getIDByAddress(*en)); 00322 s << *en << ";"; 00323 en++; 00324 } 00325 00326 // check if the result is an integer 00327 try 00328 { 00329 int intval = boost::lexical_cast<short>(s.str()); 00330 Tuple tu; 00331 tu.push_back(ID::termFromInteger(intval)); 00332 answer.get().push_back(tu); 00333 } 00334 catch(const boost::bad_lexical_cast &) 00335 { 00336 // not an integer 00337 Term resultterm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + s.str() + "\""); 00338 Tuple tu; 00339 tu.push_back(registry->storeTerm(resultterm)); 00340 // the next line would also work and be more efficient, but the above line tests more 00341 //tu.push_back(registry->storeConstOrVarTerm(resultterm)); 00342 answer.get().push_back(tu); 00343 } 00344 } 00345 }; 00346 00347 class TestListDomainAtom: 00348 public PluginAtom 00349 { 00350 public: 00351 TestListDomainAtom(): 00352 PluginAtom("testListDomain", true) // monotonic, as there is no predicate input anyway 00353 { 00354 addInputTuple(); 00355 setOutputArity(1); 00356 00357 prop.functional = true; 00358 } 00359 00360 std::vector<std::string> permute(std::vector<std::string> input){ 00361 00362 if (input.size() > 0){ 00363 std::vector<std::string> res; 00364 for (uint32_t i = 0; i < input.size(); ++i){ 00365 DBGLOG(DBG, "Choosing " << i); 00366 std::vector<std::string> i2 = input; 00367 i2.erase(i2.begin() + i); 00368 BOOST_FOREACH (std::string subperm, permute(i2)){ 00369 std::stringstream ss; 00370 ss << input[i] << (subperm.length() > 0 ? ";" : "") << subperm; 00371 DBGLOG(DBG, "Permutation: " << ss.str()); 00372 res.push_back(ss.str()); 00373 00374 ss.str(""); 00375 ss << subperm; 00376 DBGLOG(DBG, "Permutation: " << ss.str()); 00377 res.push_back(ss.str()); 00378 } 00379 } 00380 return res; 00381 }else{ 00382 input.push_back(""); 00383 return input; 00384 } 00385 } 00386 00387 virtual void retrieve(const Query& query, Answer& answer) 00388 { 00389 const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString(); 00390 00391 // extract the list elements 00392 DBGLOG(DBG, "Computing elements in " << str); 00393 std::vector<std::string> elements; 00394 std::stringstream element; 00395 for (uint32_t i = 0; i <= str.length(); i++){ 00396 if (str[i] == ';' || str[i] == '\0'){ 00397 DBGLOG(DBG, "Delimiter detected; element: " << element.str()); 00398 if (element.str().length() > 0) elements.push_back(element.str()); 00399 element.str(""); 00400 }else{ 00401 DBGLOG(DBG, "Consuming character " << str[i]); 00402 element << str[i]; 00403 } 00404 } 00405 00406 // compute all permutations and return them 00407 DBGLOG(DBG, "Computing permutations over " << elements.size() << " elements"); 00408 BOOST_FOREACH (std::string perm, permute(elements)){ 00409 Term t(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + perm + "\""); 00410 Tuple tu; 00411 tu.push_back(registry->storeTerm(t)); 00412 answer.get().push_back(tu); 00413 } 00414 } 00415 }; 00416 00417 class TestListConcatAtom: 00418 public PluginAtom 00419 { 00420 public: 00421 TestListConcatAtom(): 00422 PluginAtom("testListConcat", true) // monotonic, as there is no predicate input anyway 00423 { 00424 addInputTuple(); 00425 setOutputArity(1); 00426 00427 prop.functional = true; 00428 } 00429 00430 virtual void retrieve(const Query& query, Answer& answer) 00431 { 00432 std::stringstream ss; 00433 00434 BOOST_FOREACH(ID tid, query.input) 00435 { 00436 assert(tid.isTerm()); 00437 if( tid.isIntegerTerm() ) 00438 ss << tid.address; 00439 else if( tid.isConstantTerm() ) 00440 { 00441 const std::string& str = registry->terms.getByID(tid).getUnquotedString(); 00442 if (ss.str().length() > 0) ss << ";"; 00443 ss << str; 00444 } 00445 else 00446 throw PluginError("encountered unknown term type!"); 00447 } 00448 00449 Term resultterm(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + ss.str() + "\""); 00450 Tuple tu; 00451 tu.push_back(registry->storeTerm(resultterm)); 00452 00453 answer.get().push_back(tu); 00454 } 00455 }; 00456 00457 class TestListLengthAtom: 00458 public PluginAtom 00459 { 00460 public: 00461 TestListLengthAtom(): 00462 PluginAtom("testListLength", true) 00463 { 00464 addInputConstant(); 00465 addInputConstant(); 00466 setOutputArity(1); 00467 00468 prop.functional = true; 00469 } 00470 00471 virtual void retrieve(const Query& query, Answer& answer) 00472 { 00473 const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString(); 00474 int len = 0; 00475 if (str.length() > 0) len++; 00476 for (uint32_t i = 0; i < str.length(); i++){ 00477 if (str[i] == ';') len++; 00478 } 00479 00480 Tuple tu; 00481 tu.push_back(ID::termFromInteger(len / query.input[1].address)); 00482 answer.get().push_back(tu); 00483 } 00484 }; 00485 00486 00487 class TestListSplitAtom: 00488 public PluginAtom 00489 { 00490 public: 00491 TestListSplitAtom(): 00492 PluginAtom("testListSplit", true) // monotonic, as there is no predicate input anyway 00493 { 00494 addInputConstant(); 00495 addInputConstant(); 00496 setOutputArity(2); 00497 00498 prop.functional = true; 00499 prop.wellorderingStrlen.insert(std::pair<int, int>(0, 0)); 00500 prop.wellorderingStrlen.insert(std::pair<int, int>(0, 1)); 00501 } 00502 00503 virtual void retrieve(const Query& query, Answer& answer) 00504 { 00505 const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString(); 00506 int cnt = query.input[1].address; 00507 00508 std::stringstream substr1, substr2; 00509 int nr = 0; 00510 for (uint32_t i = 0; i < str.length(); i++){ 00511 if (str[i] == ';'){ 00512 nr++; 00513 if (nr == cnt) continue; 00514 } 00515 if (nr >= cnt) substr2 << str[i]; 00516 else substr1 << str[i]; 00517 } 00518 00519 Term t1(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr1.str() + "\""); 00520 Term t2(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr2.str() + "\""); 00521 Tuple tu; 00522 tu.push_back(registry->storeTerm(t1)); 00523 tu.push_back(registry->storeTerm(t2)); 00524 answer.get().push_back(tu); 00525 } 00526 }; 00527 00528 00529 class TestListSplitHalfAtom: 00530 public PluginAtom 00531 { 00532 public: 00533 TestListSplitHalfAtom(): 00534 PluginAtom("testListSplitHalf", true) // monotonic, as there is no predicate input anyway 00535 { 00536 addInputConstant(); 00537 setOutputArity(2); 00538 00539 prop.functional = true; 00540 prop.wellorderingStrlen.insert(std::pair<int, int>(0, 0)); 00541 prop.wellorderingStrlen.insert(std::pair<int, int>(0, 1)); 00542 } 00543 00544 virtual void retrieve(const Query& query, Answer& answer) 00545 { 00546 const std::string& str = registry->terms.getByID(query.input[0]).getUnquotedString(); 00547 00548 int len = 0; 00549 if (str.length() > 0) len++; 00550 for (uint32_t i = 0; i < str.length(); i++){ 00551 if (str[i] == ';') len++; 00552 } 00553 00554 int cnt = len / 2; 00555 00556 std::stringstream substr1, substr2; 00557 int nr = 0; 00558 for (uint32_t i = 0; i < str.length(); i++){ 00559 if (str[i] == ';'){ 00560 nr++; 00561 if (nr == cnt) continue; 00562 } 00563 if (nr >= cnt) substr2 << str[i]; 00564 else substr1 << str[i]; 00565 } 00566 00567 Term t1(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr1.str() + "\""); 00568 Term t2(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + substr2.str() + "\""); 00569 Tuple tu; 00570 tu.push_back(registry->storeTerm(t1)); 00571 tu.push_back(registry->storeTerm(t2)); 00572 answer.get().push_back(tu); 00573 } 00574 }; 00575 00576 class TestListMergeAtom: 00577 public PluginAtom 00578 { 00579 public: 00580 TestListMergeAtom(): 00581 PluginAtom("testListMerge", true) // monotonic, as there is no predicate input anyway 00582 { 00583 addInputConstant(); 00584 addInputConstant(); 00585 addInputConstant(); 00586 setOutputArity(2); 00587 00588 prop.functional = true; 00589 } 00590 00591 virtual void retrieve(const Query& query, Answer& answer) 00592 { 00593 const std::string& str1 = registry->terms.getByID(query.input[1]).getUnquotedString(); 00594 const std::string& str2 = registry->terms.getByID(query.input[2]).getUnquotedString(); 00595 00596 std::stringstream element1, element2, merged; 00597 uint32_t c1 = 0; 00598 uint32_t c2 = 0; 00599 00600 std::vector<std::string> list1, list2; 00601 00602 // extract list 1 00603 while(str1[c1] != '\0'){ 00604 if (str1[c1] == ';') c1++; 00605 element1.str(""); 00606 while (str1[c1] != ';' && str1[c1] != '\0'){ 00607 element1 << str1[c1]; 00608 c1++; 00609 } 00610 list1.push_back(element1.str()); 00611 } 00612 00613 // extract list 2 00614 while(str2[c2] != '\0'){ 00615 if (str2[c2] == ';') c2++; 00616 element2.str(""); 00617 while (str2[c2] != ';' && str2[c2] != '\0'){ 00618 element2 << str2[c2]; 00619 c2++; 00620 } 00621 list2.push_back(element2.str()); 00622 } 00623 00624 // merge 00625 c1 = 0; 00626 c2 = 0; 00627 while (c1 < list1.size() || c2 < list2.size()){ 00628 if (c1 > 0 || c2 > 0) merged << ";"; 00629 if (c1 == list1.size()){ 00630 merged << list2[c2]; 00631 c2++; 00632 } 00633 else if (c2 == list2.size()){ 00634 merged << list1[c1]; 00635 c1++; 00636 } 00637 else if (list1[c1].compare(list2[c2]) < 0){ 00638 merged << list1[c1]; 00639 c1++; 00640 } 00641 else{ 00642 assert(list1[c1].compare(list2[c2]) >= 0); 00643 merged << list2[c2]; 00644 c2++; 00645 } 00646 } 00647 00648 Term t(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "\"" + merged.str() + "\""); 00649 Tuple tu; 00650 tu.push_back(query.input[0]); 00651 tu.push_back(registry->storeTerm(t)); 00652 answer.get().push_back(tu); 00653 } 00654 }; 00655 00656 class TestSubstrAtom: 00657 public PluginAtom 00658 { 00659 public: 00660 TestSubstrAtom(): 00661 PluginAtom("testSubstr", true) // monotonic, as there is no predicate input anyway 00662 { 00663 addInputConstant(); 00664 addInputConstant(); 00665 addInputConstant(); 00666 setOutputArity(1); 00667 00668 prop.functional = true; 00669 prop.wellorderingStrlen.insert(std::pair<int, int>(0, 0)); 00670 } 00671 00672 virtual void retrieve(const Query& query, Answer& answer) 00673 { 00674 if (!query.input[1].isIntegerTerm()) throw GeneralError("testSubstr expects an integer as its second argument"); 00675 if (!query.input[2].isIntegerTerm()) throw GeneralError("testSubstr expects an integer as its third argument"); 00676 00677 try{ 00678 int start = query.input[1].address; 00679 int len = query.input[2].address; 00680 std::string str = registry->terms.getByID(query.input[0]).getUnquotedString(); 00681 int clen = str.length() - start; 00682 if (clen < len) len = clen; 00683 std::string substring = str.substr(start, len); 00684 if (registry->terms.getByID(query.input[0]).isQuotedString()) substring = "\"" + substring + "\""; 00685 ID resultterm = registry->storeConstantTerm(substring); 00686 Tuple tu; 00687 tu.push_back(resultterm); 00688 answer.get().push_back(tu); 00689 }catch(...){ 00690 // specified substring is out of bounds 00691 // return nothing 00692 } 00693 } 00694 }; 00695 00696 class TestSmallerThanAtom: 00697 public PluginAtom 00698 { 00699 public: 00700 TestSmallerThanAtom(): 00701 PluginAtom("testSmallerThan", true) // monotonic, as there is no predicate input anyway 00702 { 00703 addInputConstant(); 00704 addInputConstant(); 00705 setOutputArity(0); 00706 00707 prop.functional = true; 00708 } 00709 00710 virtual void retrieve(const Query& query, Answer& answer) 00711 { 00712 if (query.input[0].isIntegerTerm() && query.input[1].isIntegerTerm()){ 00713 // integer comparison 00714 if (query.input[0].address < query.input[1].address){ 00715 Tuple tu; 00716 answer.get().push_back(tu); 00717 } 00718 }else{ 00719 // string comparison 00720 std::string str1 = registry->terms.getByID(query.input[0]).getUnquotedString(); 00721 std::string str2 = registry->terms.getByID(query.input[1]).getUnquotedString(); 00722 if (str1.compare(str2) < 0){ 00723 Tuple tu; 00724 answer.get().push_back(tu); 00725 } 00726 } 00727 } 00728 }; 00729 00730 class TestFirstAtom: 00731 public PluginAtom 00732 { 00733 public: 00734 TestFirstAtom(): 00735 PluginAtom("testFirst", true) // monotonic, as there is no predicate input anyway 00736 { 00737 addInputConstant(); 00738 setOutputArity(2); 00739 00740 prop.functional = true; 00741 } 00742 00743 virtual void retrieve(const Query& query, Answer& answer) 00744 { 00745 std::string str = registry->terms.getByID(query.input[0]).getUnquotedString(); 00746 00747 Tuple tu; 00748 tu.push_back(registry->storeConstantTerm("\"" + (str.length() >= 1 ? str.substr(0, 1) : "") + "\"", true)); 00749 tu.push_back(registry->storeConstantTerm("\"" + (str.length() >= 1 ? str.substr(1) : "") + "\"", true)); 00750 answer.get().push_back(tu); 00751 } 00752 }; 00753 00754 class TestPushAtom: 00755 public PluginAtom 00756 { 00757 public: 00758 TestPushAtom(): 00759 PluginAtom("testPush", true) // monotonic, as there is no predicate input anyway 00760 { 00761 addInputConstant(); 00762 addInputConstant(); 00763 setOutputArity(1); 00764 00765 prop.functional = true; 00766 } 00767 00768 virtual void retrieve(const Query& query, Answer& answer) 00769 { 00770 std::string str1 = registry->terms.getByID(query.input[0]).getUnquotedString(); 00771 std::string str2 = registry->terms.getByID(query.input[1]).getUnquotedString(); 00772 00773 Tuple tu; 00774 tu.push_back(registry->storeConstantTerm("\"" + str1 + str2 + "\"")); 00775 answer.get().push_back(tu); 00776 } 00777 }; 00778 00779 class TestMoveAtom: 00780 public PluginAtom 00781 { 00782 public: 00783 TestMoveAtom(): 00784 PluginAtom("testMove", true) // monotonic, as there is no predicate input anyway 00785 { 00786 addInputPredicate(); 00787 addInputConstant(); 00788 addInputConstant(); 00789 addInputConstant(); 00790 setOutputArity(2); 00791 00792 prop.functional = true; 00793 } 00794 00795 virtual void retrieve(const Query& query, Answer& answer) 00796 { 00797 ID state = query.input[1]; 00798 std::string ichar = registry->terms.getByID(query.input[2]).getUnquotedString(); 00799 std::string schar = registry->terms.getByID(query.input[3]).getUnquotedString(); 00800 00801 // go through the tuples of the accessability relation 00802 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 00803 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 00804 while (en < en_end){ 00805 const OrdinaryAtom& oatom = registry->ogatoms.getByAddress(*en); 00806 // match? 00807 if (oatom.tuple[1] == state && 00808 registry->terms.getByID(oatom.tuple[2]).getUnquotedString() == ichar && 00809 registry->terms.getByID(oatom.tuple[3]).getUnquotedString() == schar){ 00810 00811 // go to this state 00812 Tuple tu; 00813 tu.push_back(oatom.tuple[4]); 00814 tu.push_back(oatom.tuple[5]); 00815 answer.get().push_back(tu); 00816 } 00817 en++; 00818 } 00819 } 00820 }; 00821 00822 00823 class TestStrlenAtom: 00824 public PluginAtom 00825 { 00826 public: 00827 TestStrlenAtom(): 00828 PluginAtom("testStrlen", true) // monotonic, as there is no predicate input anyway 00829 { 00830 addInputConstant(); 00831 setOutputArity(1); 00832 00833 prop.functional = true; 00834 prop.finiteFiber = true; 00835 } 00836 00837 virtual void retrieve(const Query& query, Answer& answer) 00838 { 00839 Tuple tu; 00840 tu.push_back(ID::termFromInteger(registry->terms.getByID(query.input[0]).getUnquotedString().length())); 00841 answer.get().push_back(tu); 00842 } 00843 }; 00844 00845 class TestSetMinusAtom: 00846 public ComfortPluginAtom 00847 { 00848 public: 00849 TestSetMinusAtom(): 00850 // this nonmonotonicity is very important, 00851 // because this atom is definitively nonmonotonic 00852 // and there are testcases that fail if this is set to true! 00853 ComfortPluginAtom("testSetMinus", false) 00854 { 00855 addInputPredicate(); 00856 addInputPredicate(); 00857 setOutputArity(1); 00858 prop.monotonicInputPredicates.insert(0); 00859 prop.antimonotonicInputPredicates.insert(1); 00860 prop.finiteOutputDomain.insert(0); 00861 } 00862 00863 virtual void retrieve(const ComfortQuery& query, ComfortAnswer& answer) 00864 { 00865 assert(query.input.size() == 2); 00866 if( !query.input[0].isConstant() || !query.input[1].isConstant() ) 00867 throw PluginError("need constant predicates as input to testSetMinus!"); 00868 00869 // extract predicates from input 00870 std::vector<ComfortInterpretation> psets; 00871 psets.resize(2); // allocate exactly two sets 00872 query.interpretation.matchPredicate(query.input[0].strval, psets[0]); 00873 query.interpretation.matchPredicate(query.input[1].strval, psets[1]); 00874 00875 // extract terms from predicate sets 00876 std::vector<std::set<ComfortTerm> > tsets; 00877 BOOST_FOREACH(const ComfortInterpretation& pset, psets) 00878 { 00879 // put result into termsets 00880 tsets.push_back(std::set<ComfortTerm>()); 00881 std::set<ComfortTerm>& tset = tsets.back(); 00882 00883 // extract (assume unary predicates) 00884 BOOST_FOREACH(const ComfortAtom& pred, pset) 00885 { 00886 if( pred.tuple.size() != 2 ) 00887 { 00888 std::stringstream s; 00889 s << "can only process atoms of arity 2 with testSetMinus" << 00890 "(got " << printrange(pred.tuple) << ")"; 00891 throw PluginError(s.str()); 00892 } 00893 // simply insert the argument into the set 00894 tset.insert(pred.tuple[1]); 00895 } 00896 } 00897 00898 // do set difference between tsets 00899 std::set<ComfortTerm> result; 00900 std::insert_iterator<std::set<ComfortTerm> > 00901 iit(result, result.begin()); 00902 std::set_difference( 00903 tsets[0].begin(), tsets[0].end(), 00904 tsets[1].begin(), tsets[1].end(), 00905 iit); 00906 BOOST_FOREACH(const ComfortTerm& t, result) 00907 { 00908 ComfortTuple tu; 00909 tu.push_back(t); 00910 answer.insert(tu); 00911 } 00912 } 00913 00914 class EAHeuristics : public ExternalAtomEvaluationHeuristics{ 00915 public: 00916 EAHeuristics(RegistryPtr reg) : ExternalAtomEvaluationHeuristics(reg) {} 00917 bool doEvaluate(const ExternalAtom& eatom, InterpretationConstPtr eatomMask, InterpretationConstPtr programMask, InterpretationConstPtr partialAssignment, InterpretationConstPtr assigned, InterpretationConstPtr changed) { return true; } 00918 }; 00919 00920 class EAHeuristicsFactory : public ExternalAtomEvaluationHeuristicsFactory{ 00921 public: 00922 ExternalAtomEvaluationHeuristicsPtr createHeuristics(RegistryPtr reg){ return ExternalAtomEvaluationHeuristicsPtr(new EAHeuristics(reg)); } 00923 }; 00924 00925 bool providesCustomExternalAtomEvaluationHeuristicsFactory() const { return true; } 00926 00927 ExternalAtomEvaluationHeuristicsFactoryPtr getCustomExternalAtomEvaluationHeuristicsFactory() const 00928 { return ExternalAtomEvaluationHeuristicsFactoryPtr(new EAHeuristicsFactory()); } 00929 00930 }; 00931 00932 class TestSetMinusNonComfortAtom: // tests user-defined external learning 00933 public PluginAtom 00934 { 00935 public: 00936 TestSetMinusNonComfortAtom(): 00937 PluginAtom("testSetMinusNonComfort", false) // monotonic, and no predicate inputs anyway 00938 { 00939 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 00940 addInputPredicate(); 00941 addInputPredicate(); 00942 prop.monotonicInputPredicates.insert(0); 00943 prop.antimonotonicInputPredicates.insert(1); 00944 setOutputArity(1); 00945 } 00946 00947 virtual void retrieve(const Query& query, Answer& answer) 00948 { 00949 static std::map<std::string, ID> ruleIDs; 00950 00951 // find relevant input 00952 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 00953 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 00954 00955 std::vector<Tuple> tuples1; 00956 std::vector<Tuple> tuples2; 00957 while (en < en_end){ 00958 00959 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 00960 Tuple tu; 00961 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 00962 tu.push_back(atom.tuple[i]); 00963 } 00964 if (atom.tuple[0] == query.input[0]){ 00965 tuples1.push_back(tu); 00966 } 00967 if (atom.tuple[0] == query.input[1]){ 00968 tuples2.push_back(tu); 00969 } 00970 en++; 00971 } 00972 00973 // Learning of the nogoods 00974 BOOST_FOREACH (Tuple t, tuples1){ 00975 if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){ 00976 answer.get().push_back(t); 00977 } 00978 } 00979 } 00980 }; 00981 00982 class TestSetMinusPartialAtom: // tests user-defined external learning 00983 public PluginAtom 00984 { 00985 public: 00986 TestSetMinusPartialAtom(): 00987 PluginAtom("testSetMinusPartial", false) // monotonic, and no predicate inputs anyway 00988 { 00989 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 00990 addInputPredicate(); 00991 addInputPredicate(); 00992 prop.monotonicInputPredicates.insert(0); 00993 prop.antimonotonicInputPredicates.insert(1); 00994 prop.setProvidesPartialAnswer(true); 00995 setOutputArity(1); 00996 } 00997 00998 virtual void retrieve(const Query& query, Answer& answer) 00999 { 01000 static std::map<std::string, ID> ruleIDs; 01001 01002 // find relevant input 01003 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first(); 01004 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end(); 01005 01006 std::vector<Tuple> tuples1true; 01007 std::vector<Tuple> tuples1unknown; 01008 std::vector<Tuple> tuples2true; 01009 std::vector<Tuple> tuples2unknown; 01010 while (en < en_end){ 01011 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01012 Tuple tu; 01013 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 01014 tu.push_back(atom.tuple[i]); 01015 } 01016 if (!query.assigned || query.assigned->getFact(*en) ) { 01017 // assigned 01018 if (query.interpretation->getFact(*en) ){ 01019 // assigned to true? 01020 if (atom.tuple[0] == query.input[0]){ 01021 tuples1true.push_back(tu); 01022 } 01023 if (atom.tuple[0] == query.input[1]){ 01024 tuples2true.push_back(tu); 01025 } 01026 } 01027 }else{ 01028 // not assigned 01029 if (atom.tuple[0] == query.input[0]){ 01030 tuples1unknown.push_back(tu); 01031 } 01032 if (atom.tuple[0] == query.input[1]){ 01033 tuples2unknown.push_back(tu); 01034 } 01035 } 01036 en++; 01037 } 01038 01039 // Learning of the nogoods 01040 BOOST_FOREACH (Tuple t, tuples1true){ 01041 if (std::find(tuples2true.begin(), tuples2true.end(), t) != tuples2true.end()){ 01042 // true in first predicate, true in second --> false in the result 01043 }else if (std::find(tuples2unknown.begin(), tuples2unknown.end(), t) != tuples2unknown.end()){ 01044 // true in first predicate, unknown in second --> unknown in the result 01045 answer.getUnknown().push_back(t); 01046 }else{ 01047 // true in first predicate, false in second --> true in the result 01048 answer.get().push_back(t); 01049 } 01050 } 01051 BOOST_FOREACH (Tuple t, tuples1unknown){ 01052 if (std::find(tuples2true.begin(), tuples2true.end(), t) == tuples2true.end()){ 01053 // unknown in first predicate, false or unknown in second --> unknown in the result 01054 answer.getUnknown().push_back(t); 01055 } 01056 } 01057 // false in the first predicate --> false in the result 01058 } 01059 }; 01060 01061 class TestSetMinusNogoodBasedLearningAtom: // tests user-defined external learning 01062 public PluginAtom 01063 { 01064 public: 01065 TestSetMinusNogoodBasedLearningAtom(): 01066 PluginAtom("testSetMinusNogoodBasedLearning", false) // monotonic, and no predicate inputs anyway 01067 { 01068 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01069 addInputPredicate(); 01070 addInputPredicate(); 01071 setOutputArity(1); 01072 } 01073 01074 virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods) 01075 { 01076 static std::map<std::string, ID> ruleIDs; 01077 01078 // find relevant input 01079 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01080 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01081 01082 std::vector<Tuple> tuples1; 01083 std::vector<Tuple> tuples2; 01084 while (en < en_end){ 01085 01086 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01087 Tuple tu; 01088 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 01089 tu.push_back(atom.tuple[i]); 01090 } 01091 if (atom.tuple[0] == query.input[0]){ 01092 tuples1.push_back(tu); 01093 } 01094 if (atom.tuple[0] == query.input[1]){ 01095 tuples2.push_back(tu); 01096 } 01097 en++; 01098 } 01099 01100 // Learning of the nogoods 01101 BOOST_FOREACH (Tuple t, tuples1){ 01102 if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){ 01103 answer.get().push_back(t); 01104 01105 // Test: Learning based on direct definition of nogoods 01106 if (nogoods != NogoodContainerPtr()){ 01107 if (query.ctx->config.getOption("ExternalLearningUser")){ 01108 // learn that presence of t in query.input[0] and absence in query.input[1] implies presence in output 01109 OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 01110 at1.tuple.push_back(query.input[0]); 01111 for (uint32_t i = 0; i < t.size(); ++i) at1.tuple.push_back(t[i]); 01112 OrdinaryAtom at2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 01113 at2.tuple.push_back(query.input[1]); 01114 for (uint32_t i = 0; i < t.size(); ++i) at2.tuple.push_back(t[i]); 01115 01116 Nogood nogood; 01117 nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryGAtom(at1).address, true)); 01118 nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryGAtom(at2).address, false)); 01119 nogood.insert(ExternalLearningHelper::getOutputAtom(query, t, false)); 01120 nogoods->addNogood(nogood); 01121 01122 DBGLOG(DBG, "Learned user-defined nogood: " << nogood); 01123 }else{ 01124 DBGLOG(DBG, "No user-defined learning"); 01125 } 01126 }else{ 01127 DBGLOG(DBG, "No user-defined learning"); 01128 } 01129 } 01130 } 01131 } 01132 }; 01133 01134 class TestSetMinusNongroundNogoodBasedLearningAtom: // tests user-defined external learning 01135 public PluginAtom 01136 { 01137 01138 public: 01139 01140 TestSetMinusNongroundNogoodBasedLearningAtom(): 01141 PluginAtom("testSetMinusNongroundNogoodBasedLearning", false) // monotonic, and no predicate inputs anyway 01142 { 01143 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01144 addInputPredicate(); 01145 addInputPredicate(); 01146 prop.monotonicInputPredicates.insert(0); 01147 prop.antimonotonicInputPredicates.insert(1); 01148 setOutputArity(1); 01149 } 01150 01151 virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods) 01152 { 01153 static std::map<std::string, ID> ruleIDs; 01154 01155 int arity = -1; 01156 01157 // find relevant input 01158 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01159 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01160 01161 std::vector<Tuple> tuples1; 01162 std::vector<Tuple> tuples2; 01163 while (en < en_end){ 01164 01165 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01166 arity = atom.tuple.size() - 1; 01167 Tuple tu; 01168 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 01169 tu.push_back(atom.tuple[i]); 01170 } 01171 if (atom.tuple[0] == query.input[0]){ 01172 tuples1.push_back(tu); 01173 } 01174 if (atom.tuple[0] == query.input[1]){ 01175 tuples2.push_back(tu); 01176 } 01177 en++; 01178 } 01179 01180 BOOST_FOREACH (Tuple t, tuples1){ 01181 if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){ 01182 answer.get().push_back(t); 01183 } 01184 } 01185 01186 // Test: Learning based on direct definition of nogoods 01187 if (nogoods != NogoodContainerPtr() && arity > -1){ 01188 if (query.ctx->config.getOption("ExternalLearningUser")){ 01189 // learn that presence of t in query.input[0] and absence in query.input[1] implies presence in output 01190 OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 01191 OrdinaryAtom at2(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 01192 at1.tuple.push_back(query.input[0]); 01193 at2.tuple.push_back(query.input[1]); 01194 Tuple t; 01195 for (int i = 0; i < arity; ++i){ 01196 std::stringstream var; 01197 var << "X" << i; 01198 at1.tuple.push_back(getRegistry()->storeVariableTerm(var.str())); 01199 at2.tuple.push_back(getRegistry()->storeVariableTerm(var.str())); 01200 t.push_back(getRegistry()->storeVariableTerm(var.str())); 01201 } 01202 01203 Nogood nogood; 01204 nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryNAtom(at1).address, true, false)); 01205 nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryNAtom(at2).address, false, false)); 01206 nogood.insert(NogoodContainer::createLiteral(ExternalLearningHelper::getOutputAtom(query, t, false).address, true, false)); 01207 nogoods->addNogood(nogood); 01208 01209 DBGLOG(DBG, "Learned user-defined nogood: " << nogood); 01210 }else{ 01211 DBGLOG(DBG, "No user-defined learning"); 01212 } 01213 }else{ 01214 DBGLOG(DBG, "No user-defined learning"); 01215 } 01216 } 01217 }; 01218 01219 class TestSetMinusRuleBasedLearningAtom: // tests user-defined external learning 01220 public PluginAtom 01221 { 01222 private: 01223 ProgramCtx* ctx; 01224 01225 public: 01226 TestSetMinusRuleBasedLearningAtom(ProgramCtx* ctx): 01227 ctx(ctx), 01228 PluginAtom("testSetMinusRuleBasedLearning", false) // monotonic, and no predicate inputs anyway 01229 { 01230 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01231 addInputPredicate(); 01232 addInputPredicate(); 01233 setOutputArity(1); 01234 } 01235 01236 virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods) 01237 { 01238 static std::map<std::string, ID> ruleIDs; 01239 01240 // find relevant input 01241 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01242 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01243 01244 std::vector<Tuple> tuples1; 01245 std::vector<Tuple> tuples2; 01246 while (en < en_end){ 01247 01248 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01249 Tuple tu; 01250 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 01251 tu.push_back(atom.tuple[i]); 01252 } 01253 if (atom.tuple[0] == query.input[0]){ 01254 tuples1.push_back(tu); 01255 } 01256 if (atom.tuple[0] == query.input[1]){ 01257 tuples2.push_back(tu); 01258 } 01259 en++; 01260 } 01261 01262 // Test: Rule-based learning 01263 if (nogoods != NogoodContainerPtr()){ 01264 if (ctx->config.getOption("ExternalLearningUser")){ 01265 std::string rule = "out(X) :- in1(X), not in2(X)."; 01266 01267 if (ruleIDs.find(rule) == ruleIDs.end()){ 01268 ruleIDs[rule] = ExternalLearningHelper::getIDOfLearningRule(ctx, rule); 01269 } 01270 ID rid = ruleIDs[rule]; 01271 if (rid == ID_FAIL){ 01272 DBGLOG(DBG, "Could not learn from rule because parsing failed"); 01273 exit(0); 01274 }else{ 01275 ExternalLearningHelper::learnFromRule(query, rid, ctx, nogoods); 01276 } 01277 } 01278 } 01279 01280 BOOST_FOREACH (Tuple t, tuples1){ 01281 if (std::find(tuples2.begin(), tuples2.end(), t) == tuples2.end()){ 01282 answer.get().push_back(t); 01283 } 01284 } 01285 } 01286 }; 01287 01288 class TestNonmonAtom: // tests user-defined external learning 01289 public PluginAtom 01290 { 01291 public: 01292 TestNonmonAtom(): 01293 PluginAtom("testNonmon", false) // monotonic, and no predicate inputs anyway 01294 { 01295 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01296 addInputPredicate(); 01297 setOutputArity(1); 01298 01299 prop.finiteOutputDomain.insert(0); 01300 } 01301 01302 virtual void retrieve(const Query& query, Answer& answer) 01303 { 01304 // find relevant input 01305 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01306 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01307 01308 std::vector<Tuple> tuples; 01309 while (en < en_end){ 01310 01311 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01312 Tuple tu; 01313 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 01314 tu.push_back(atom.tuple[i]); 01315 } 01316 if (tu.size() != 1) throw PluginError("TestNonmonAtom can only process input predicates with arity 1!"); 01317 tuples.push_back(tu); 01318 en++; 01319 } 01320 01321 Tuple t1, t2; 01322 t1.push_back(ID::termFromInteger(1)); 01323 t2.push_back(ID::termFromInteger(2)); 01324 01325 // {} -> {2}, {1} -> {1}, {2} -> {1}, {1,2} -> {1,2} 01326 if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){ 01327 answer.get().push_back(t2); 01328 } 01329 if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){ 01330 answer.get().push_back(t1); 01331 } 01332 if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){ 01333 answer.get().push_back(t1); 01334 } 01335 if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){ 01336 answer.get().push_back(t1); 01337 answer.get().push_back(t2); 01338 } 01339 } 01340 }; 01341 01342 class TestNonmon2Atom: // tests user-defined external learning 01343 public PluginAtom 01344 { 01345 public: 01346 TestNonmon2Atom(): 01347 PluginAtom("testNonmon2", false) // monotonic, and no predicate inputs anyway 01348 { 01349 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01350 addInputPredicate(); 01351 setOutputArity(1); 01352 } 01353 01354 virtual void retrieve(const Query& query, Answer& answer) 01355 { 01356 // find relevant input 01357 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01358 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01359 01360 std::vector<Tuple> tuples; 01361 while (en < en_end){ 01362 01363 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01364 Tuple tu; 01365 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 01366 tu.push_back(atom.tuple[i]); 01367 } 01368 if (tu.size() != 1) throw PluginError("TestNonmon2Atom can only process input predicates with arity 1!"); 01369 tuples.push_back(tu); 01370 en++; 01371 } 01372 01373 Tuple t1, t2; 01374 t1.push_back(ID::termFromInteger(1)); 01375 t2.push_back(ID::termFromInteger(2)); 01376 01377 // {} -> {2}, {1} -> {2}, {2} -> {}, {1,2} -> {1,2} 01378 if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){ 01379 answer.get().push_back(t2); 01380 } 01381 if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) == tuples.end()){ 01382 answer.get().push_back(t2); 01383 } 01384 if (std::find(tuples.begin(), tuples.end(), t1) == tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){ 01385 } 01386 if (std::find(tuples.begin(), tuples.end(), t1) != tuples.end() && std::find(tuples.begin(), tuples.end(), t2) != tuples.end()){ 01387 answer.get().push_back(t1); 01388 answer.get().push_back(t2); 01389 } 01390 } 01391 }; 01392 01393 class TestIdAtom: // tests user-defined external learning 01394 public PluginAtom 01395 { 01396 public: 01397 TestIdAtom(): 01398 PluginAtom("id", false) // monotonic 01399 { 01400 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01401 addInputPredicate(); 01402 setOutputArity(1); 01403 } 01404 01405 virtual void retrieve(const Query& query, Answer& answer) 01406 { 01407 // find relevant input 01408 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01409 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01410 01411 while (en < en_end){ 01412 01413 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01414 if (atom.tuple.size() != 2) throw PluginError("TestIdAtom can only process input predicates with arity 1!"); 01415 Tuple tu; 01416 tu.push_back(atom.tuple[1]); 01417 answer.get().push_back(tu); 01418 en++; 01419 } 01420 } 01421 }; 01422 01423 class TestIdpAtom: // tests user-defined external learning 01424 public PluginAtom 01425 { 01426 public: 01427 TestIdpAtom(): 01428 PluginAtom("idp", false) // monotonic 01429 { 01430 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01431 addInputPredicate(); 01432 setOutputArity(1); 01433 01434 prop.setProvidesPartialAnswer(true); 01435 } 01436 01437 virtual void retrieve(const Query& query, Answer& answer) 01438 { 01439 // find relevant input 01440 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first(); 01441 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end(); 01442 01443 while (en < en_end){ 01444 01445 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01446 if (atom.tuple.size() != 2) throw PluginError("TestIdpAtom can only process input predicates with arity 1!"); 01447 01448 Tuple tu; 01449 tu.push_back(atom.tuple[1]); 01450 if (query.interpretation->getFact(*en)) answer.get().push_back(tu); 01451 else answer.getUnknown().push_back(tu); 01452 en++; 01453 } 01454 } 01455 }; 01456 01457 class TestIdcAtom: // tests user-defined external learning 01458 public PluginAtom 01459 { 01460 public: 01461 TestIdcAtom(): 01462 PluginAtom("idc", false) // monotonic, and no predicate inputs anyway 01463 { 01464 addInputConstant(); 01465 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01466 setOutputArity(1); 01467 } 01468 01469 virtual void retrieve(const Query& query, Answer& answer) 01470 { 01471 Tuple tu; 01472 tu.push_back(query.input[0]); 01473 answer.get().push_back(tu); 01474 } 01475 }; 01476 01477 class TestNegAtom: // tests user-defined external learning 01478 public PluginAtom 01479 { 01480 public: 01481 TestNegAtom(): 01482 PluginAtom("neg", false) 01483 { 01484 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01485 addInputConstant(); 01486 addInputPredicate(); 01487 setOutputArity(1); 01488 } 01489 01490 virtual void retrieve(const Query& query, Answer& answer) 01491 { 01492 // find relevant input 01493 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01494 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01495 01496 while (en < en_end){ 01497 01498 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01499 if (atom.tuple.size() != 2) throw PluginError("TestNegAtom can only process input predicates with arity 1!"); 01500 if (atom.tuple[1] == query.input[0]){ 01501 return; 01502 } 01503 en++; 01504 } 01505 Tuple tu; 01506 tu.push_back(query.input[0]); 01507 answer.get().push_back(tu); 01508 } 01509 }; 01510 01511 class TestMinusOneAtom: 01512 public ComfortPluginAtom 01513 { 01514 public: 01515 TestMinusOneAtom(): 01516 // monotonic, as only constant inputs 01517 ComfortPluginAtom("testMinusOne", true) 01518 { 01519 addInputConstant(); 01520 setOutputArity(1); 01521 } 01522 01523 virtual void 01524 retrieve(const ComfortQuery& query, ComfortAnswer& answer) 01525 { 01526 assert(query.input.size() == 1); 01527 if( !query.input[0].isInteger() ) 01528 throw PluginError("TestMinusOneAtom can only process integer inputs!"); 01529 01530 int i = query.input[0].intval; 01531 if( i > 0 ) 01532 i--; 01533 01534 ComfortTuple t; 01535 t.push_back(ComfortTerm::createInteger(i)); 01536 answer.insert(t); 01537 } 01538 }; 01539 01540 01541 class TestEvenAtom: 01542 public ComfortPluginAtom 01543 { 01544 public: 01545 TestEvenAtom(): 01546 ComfortPluginAtom("testEven", false) 01547 { 01548 addInputPredicate(); 01549 addInputPredicate(); 01550 setOutputArity(0); 01551 } 01552 01553 virtual void 01554 retrieve(const ComfortQuery& query, ComfortAnswer& answer) 01555 { 01556 // Even is true, iff input predicates hold for even individuals 01557 01558 if (query.interpretation.size() % 2 == 0) 01559 { 01560 // succeed by returning an empty tuple 01561 answer.insert(ComfortTuple()); 01562 } 01563 else 01564 { 01565 // fail by returning no tuple 01566 } 01567 } 01568 }; 01569 01570 class TestOddAtom: 01571 public ComfortPluginAtom 01572 { 01573 public: 01574 TestOddAtom(): 01575 ComfortPluginAtom("testOdd", false) 01576 { 01577 addInputPredicate(); 01578 addInputPredicate(); 01579 setOutputArity(0); 01580 } 01581 01582 virtual void 01583 retrieve(const ComfortQuery& query, ComfortAnswer& answer) 01584 { 01585 if (query.interpretation.size() % 2 != 0) 01586 { 01587 // succeed by returning an empty tuple 01588 answer.insert(ComfortTuple()); 01589 } 01590 else 01591 { 01592 // fail by returning no tuple 01593 } 01594 } 01595 }; 01596 01597 class TestLessThanAtom: 01598 public PluginAtom 01599 { 01600 public: 01601 TestLessThanAtom(): 01602 PluginAtom("testLessThan", false) 01603 { 01604 addInputPredicate(); 01605 addInputPredicate(); 01606 setOutputArity(0); 01607 01608 prop.antimonotonicInputPredicates.insert(0); 01609 } 01610 01611 virtual void 01612 retrieve(const Query& query, Answer& answer) 01613 { 01614 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01615 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01616 01617 int a = 0; 01618 int b = 0; 01619 while (en < en_end){ 01620 01621 if (getRegistry()->ogatoms.getByAddress(*en).tuple[0] == query.input[0]){ 01622 a++; 01623 }else{ 01624 b++; 01625 } 01626 en++; 01627 } 01628 01629 if (a < b){ 01630 // succeed by returning an empty tuple 01631 Tuple t; 01632 answer.get().push_back(t); 01633 }else{ 01634 // fail by returning no tuple 01635 } 01636 } 01637 }; 01638 01639 class TestEqualAtom: 01640 public PluginAtom 01641 { 01642 public: 01643 TestEqualAtom(): 01644 PluginAtom("testEqual", false) 01645 { 01646 addInputPredicate(); 01647 addInputPredicate(); 01648 setOutputArity(0); 01649 01650 prop.antimonotonicInputPredicates.insert(0); 01651 } 01652 01653 virtual void 01654 retrieve(const Query& query, Answer& answer) 01655 { 01656 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01657 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01658 01659 int a = 0; 01660 int b = 0; 01661 while (en < en_end){ 01662 01663 if (getRegistry()->ogatoms.getByAddress(*en).tuple[0] == query.input[0]){ 01664 a++; 01665 }else{ 01666 b++; 01667 } 01668 en++; 01669 } 01670 01671 if (a == b){ 01672 // succeed by returning an empty tuple 01673 Tuple t; 01674 answer.get().push_back(t); 01675 }else{ 01676 // fail by returning no tuple 01677 } 01678 } 01679 }; 01680 01681 class TestTransitiveClosureAtom: 01682 public PluginAtom 01683 { 01684 public: 01685 TestTransitiveClosureAtom(): 01686 // monotonic, as only constant inputs 01687 PluginAtom("testTransitiveClosure", true) 01688 { 01689 addInputPredicate(); 01690 setOutputArity(2); 01691 01692 prop.monotonicInputPredicates.insert(0); 01693 } 01694 01695 virtual void 01696 retrieve(const Query& query, Answer& answer) 01697 { 01698 assert(query.input.size() == 1); 01699 01700 std::set<ID> nodes; 01701 std::set<std::pair<ID, ID> > edges; 01702 01703 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01704 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01705 while (en < en_end){ 01706 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en); 01707 01708 nodes.insert(ogatom.tuple[1]); 01709 nodes.insert(ogatom.tuple[2]); 01710 edges.insert(std::pair<ID, ID>(ogatom.tuple[1], ogatom.tuple[2])); 01711 en++; 01712 } 01713 01714 BOOST_FOREACH (ID n, nodes){ 01715 BOOST_FOREACH (ID m, nodes){ 01716 BOOST_FOREACH (ID o, nodes){ 01717 if (std::find(edges.begin(), edges.end(), std::pair<ID, ID>(n, m)) != edges.end() && 01718 std::find(edges.begin(), edges.end(), std::pair<ID, ID>(m, o)) != edges.end()){ 01719 Tuple t; 01720 t.push_back(n); 01721 t.push_back(o); 01722 answer.get().push_back(t); 01723 } 01724 } 01725 } 01726 } 01727 } 01728 }; 01729 01730 class TestCycleAtom: 01731 public PluginAtom 01732 { 01733 public: 01734 TestCycleAtom(): 01735 // monotonic, as only constant inputs 01736 PluginAtom("testCycle", true) 01737 { 01738 addInputPredicate(); 01739 addInputConstant(); 01740 setOutputArity(0); 01741 01742 prop.monotonicInputPredicates.insert(0); 01743 } 01744 01745 bool dfscycle(bool directed, ID parent, ID node, std::map<ID, std::set<ID> >& outedges, std::map<ID, bool>& visited, std::set<std::pair<ID, ID> >& cycle){ 01746 01747 // if the node was already visited in the dfs search, then we have a cycle 01748 if (visited[node]) return true; 01749 01750 // otherwise: visit the node 01751 visited[node] = true; 01752 01753 // visit all child nodes 01754 BOOST_FOREACH (ID child, outedges[node]){ 01755 cycle.insert(std::pair<ID, ID>(node, child)); 01756 if (directed || child != parent){ 01757 if (dfscycle(directed, node, child, outedges, visited, cycle)) return true; 01758 } 01759 cycle.erase(std::pair<ID, ID>(node, child)); 01760 } 01761 01762 visited[node] = false; 01763 return false; 01764 } 01765 01766 virtual void 01767 retrieve(const Query& query, Answer& answer) 01768 { 01769 assert(query.input.size() == 1); 01770 01771 Term dir(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, "directed"); 01772 bool directed = query.input[1] == getRegistry()->storeTerm(dir); 01773 01774 std::set<ID> nodes; 01775 std::map<ID, std::set<ID> > outedges; 01776 std::map<ID, bool> visited; 01777 std::set<std::pair<ID, ID> > cycle; 01778 01779 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01780 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01781 while (en < en_end){ 01782 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en); 01783 01784 nodes.insert(ogatom.tuple[1]); 01785 nodes.insert(ogatom.tuple[2]); 01786 outedges[ogatom.tuple[1]].insert(ogatom.tuple[2]); 01787 if (!directed) outedges[ogatom.tuple[2]].insert(ogatom.tuple[1]); 01788 en++; 01789 } 01790 01791 BOOST_FOREACH (ID n, nodes){ 01792 if (dfscycle(directed, ID_FAIL, n, outedges, visited, cycle)){ 01793 Tuple t; 01794 answer.get().push_back(t); 01795 } 01796 } 01797 } 01798 }; 01799 01800 class TestAppendAtom: 01801 public PluginAtom 01802 { 01803 public: 01804 TestAppendAtom(): 01805 PluginAtom("testAppend", true) 01806 { 01807 addInputPredicate(); 01808 addInputConstant(); 01809 setOutputArity(1); 01810 01811 prop.antimonotonicInputPredicates.insert(0); 01812 } 01813 01814 virtual void 01815 retrieve(const Query& query, Answer& answer) 01816 { 01817 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01818 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01819 01820 while (en < en_end){ 01821 01822 std::string str = getRegistry()->terms.getByID(getRegistry()->ogatoms.getByAddress(*en).tuple[1]).getUnquotedString(); 01823 str = str + getRegistry()->terms.getByID(query.input[1]).getUnquotedString(); 01824 Term term(ID::MAINKIND_TERM | ID::SUBKIND_TERM_CONSTANT, str); 01825 ID idout = getRegistry()->storeTerm(term); 01826 Tuple t; 01827 t.push_back(idout); 01828 answer.get().push_back(t); 01829 01830 en++; 01831 } 01832 } 01833 }; 01834 01835 class TestDisjAtom: 01836 public PluginAtom 01837 { 01838 public: 01839 TestDisjAtom(): 01840 PluginAtom("testDisj", false) 01841 { 01842 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 01843 addInputPredicate(); // interpretation i 01844 addInputPredicate(); // positive p 01845 addInputPredicate(); // negative n 01846 // The external atom implements the following disjunction: 01847 // (bigvee_{\vec{t} \in ext(p)} i(\vec{t})) \vee (bigvee_{\vec{t} \in ext(n)} \naf i(\vec{t})) 01848 // Example: &testDisj[i, p, n]() with p(1), p(2), n(0) is true iff i(1) \vee i(2) \vee \naf i(0) holds 01849 setOutputArity(0); 01850 } 01851 01852 virtual void retrieve(const Query& query, Answer& answer) 01853 { 01854 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01855 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01856 01857 while (en < en_end){ 01858 01859 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 01860 if (atom.tuple[0] == query.input[1]){ 01861 OrdinaryAtom iatom = atom; 01862 iatom.tuple[0] = query.input[0]; 01863 if (query.interpretation->getFact(getRegistry()->storeOrdinaryGAtom(iatom).address)){ 01864 Tuple t; 01865 answer.get().push_back(t); 01866 return; 01867 } 01868 } 01869 if (atom.tuple[0] == query.input[2]){ 01870 OrdinaryAtom iatom = atom; 01871 iatom.tuple[0] = query.input[0]; 01872 if (!query.interpretation->getFact(getRegistry()->storeOrdinaryGAtom(iatom).address)){ 01873 Tuple t; 01874 answer.get().push_back(t); 01875 return; 01876 } 01877 } 01878 en++; 01879 } 01880 } 01881 }; 01882 01883 class TestHashAtom: 01884 public PluginAtom 01885 { 01886 public: 01887 TestHashAtom(): 01888 PluginAtom("testHash", false) 01889 { 01890 addInputPredicate(); 01891 setOutputArity(1); 01892 } 01893 01894 virtual void retrieve(const Query& query, Answer& answer) 01895 { 01896 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01897 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01898 01899 std::size_t hashValue = 0; 01900 01901 while (en < en_end){ 01902 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en); 01903 BOOST_FOREACH (ID t, ogatom.tuple){ 01904 boost::hash_combine(hashValue, t.address); 01905 } 01906 en++; 01907 } 01908 01909 std::stringstream ss; 01910 ss << "h" << hashValue; 01911 Tuple t; 01912 t.push_back(getRegistry()->storeConstantTerm(ss.str())); 01913 answer.get().push_back(t); 01914 } 01915 }; 01916 01917 // just always true and takes 5 constant inputs 01918 class TestTrueMultiInpAtom: 01919 public PluginAtom 01920 { 01921 public: 01922 TestTrueMultiInpAtom(): 01923 PluginAtom("testTrueMultiInp", true) 01924 { 01925 addInputConstant(); 01926 addInputConstant(); 01927 addInputConstant(); 01928 addInputConstant(); 01929 addInputConstant(); 01930 setOutputArity(0); 01931 } 01932 01933 virtual void retrieve(const Query& query, Answer& answer) 01934 { 01935 Tuple t; 01936 answer.get().push_back(t); 01937 } 01938 }; 01939 01940 // takes 5 constant inputs, just always returns integer 1 in output 01941 class TestTrueMultiInpAtom2: 01942 public PluginAtom 01943 { 01944 public: 01945 TestTrueMultiInpAtom2(): 01946 PluginAtom("testTrueMultiInp2", true) 01947 { 01948 addInputConstant(); 01949 addInputConstant(); 01950 addInputConstant(); 01951 addInputConstant(); 01952 addInputConstant(); 01953 setOutputArity(1); 01954 } 01955 01956 virtual void retrieve(const Query& query, Answer& answer) 01957 { 01958 Tuple t; 01959 t.push_back(ID::termFromInteger(1)); 01960 answer.get().push_back(t); 01961 } 01962 }; 01963 01964 class TestReachableAtom: 01965 public PluginAtom 01966 { 01967 public: 01968 TestReachableAtom(): 01969 PluginAtom("testReachable", true) 01970 { 01971 addInputPredicate(); 01972 addInputConstant(); 01973 setOutputArity(1); 01974 01975 prop.relativeFiniteOutputDomain.insert(std::pair<int, int>(0, 0)); 01976 } 01977 01978 virtual void retrieve(const Query& query, Answer& answer) 01979 { 01980 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 01981 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 01982 01983 while (en < en_end){ 01984 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en); 01985 if (ogatom.tuple[1] == query.input[1]){ 01986 Tuple t; 01987 t.push_back(ogatom.tuple[2]); 01988 answer.get().push_back(t); 01989 } 01990 en++; 01991 } 01992 } 01993 }; 01994 01995 01996 01997 class TestDLSimulatorAtom: 01998 public PluginAtom 01999 { 02000 public: 02001 TestDLSimulatorAtom(): 02002 PluginAtom("testDLSimulator", false) 02003 { 02004 addInputConstant(); // mode: 1=concept retrieval, 0=consistency check 02005 addInputConstant(); // domain size: all even domain elements are non-Fliers, all odd domain elements are Fliers 02006 addInputPredicate(); // plus-concept Flier 02007 setOutputArity(1); // \neg Flier 02008 } 02009 02010 virtual void retrieve(const Query& query, Answer& answer) 02011 { 02012 if (query.input[0].address == 0){ 02013 // consistency check: check if an odd element is in plus-concept; if yes, then we have inconsistency 02014 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 02015 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 02016 02017 while (en < en_end){ 02018 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en); 02019 if (ogatom.tuple[1].address % 2 == 0) return; // inconsistent 02020 en++; 02021 } 02022 Tuple t; 02023 t.push_back(ID::termFromInteger(0)); 02024 answer.get().push_back(t); // consistent 02025 }else{ 02026 // concept \neg C query 02027 02028 // add all even elements up to the specified size; if inconsistent, then add also all odd elements 02029 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 02030 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 02031 02032 bool inc = false; 02033 while (en < en_end){ 02034 const OrdinaryAtom& ogatom = getRegistry()->ogatoms.getByAddress(*en); 02035 if (ogatom.tuple[1].address % 2 == 0) inc = true; // inconsistent 02036 en++; 02037 } 02038 02039 for (uint32_t i = 0; i <= query.input[1].address; ++i){ 02040 if (i % 2 == 0 || inc){ 02041 Tuple t; 02042 t.push_back(ID::termFromInteger(i)); 02043 answer.get().push_back(t); 02044 } 02045 } 02046 } 02047 } 02048 }; 02049 02050 02051 02052 02053 // Common base class for cautious and brave queries. 02054 // The only difference between answering cautious and brave queries 02055 // concerns the aggregation of the answer sets of the subprogram, 02056 // thus almost everything is implemented in this class. 02057 class TestASPQueryAtom: 02058 public PluginAtom 02059 { 02060 private: 02061 ProgramCtx& ctx; 02062 02063 public: 02064 TestASPQueryAtom(ProgramCtx& ctx, std::string atomName): 02065 ctx(ctx), PluginAtom(atomName, false /* not monotonic */) 02066 { 02067 addInputConstant(); // program file 02068 addInputPredicate(); // input interpretation 02069 addInputConstant(); // query predicate 02070 setOutputArity(0); 02071 02072 prop.variableOutputArity = true; // the output arity of this external atom depends on the arity of the query predicate 02073 prop.supportSets = true; // we provide support sets 02074 prop.onlySafeSupportSets = true; 02075 prop.completePositiveSupportSets = true; // we even provide (positive) complete support sets 02076 } 02077 02078 virtual void retrieve(const Query& query, Answer& answer) 02079 { 02080 assert(false); 02081 } 02082 02083 02084 virtual void learnSupportSets(const Query& query, NogoodContainerPtr nogoods) 02085 { 02086 Answer ans; 02087 retrieveOrLearnSupportSets(query, ans, nogoods, true); 02088 } 02089 02090 virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods) 02091 { 02092 retrieveOrLearnSupportSets(query, answer, nogoods, false); 02093 } 02094 02095 virtual void retrieveOrLearnSupportSets(const Query& query, Answer& answer, NogoodContainerPtr nogoods, bool learnSupportSets) 02096 { 02097 RegistryPtr reg = getRegistry(); 02098 02099 // input parameters to external atom &testCautiousQuery["prog", p, q](x): 02100 // query.input[0] (i.e. "prog"): filename of the program P over which we do query answering 02101 // query.input[1] (i.e. p): a predicate name; the set F of all atoms over this predicate are added to P as facts before evaluation 02102 // query.input[2] (i.e. q): name of the query predicate; the external atom will be true for all output vectors x such that q(x) is true in every answer set of P \cup F 02103 02104 // read the subprogram from the file 02105 InputProviderPtr ip(new InputProvider()); 02106 ip->addFileInput(getRegistry()->terms.getByID(query.input[0]).getUnquotedString()); 02107 02108 // prepare data structures for the subprogram P 02109 ProgramCtx pc = ctx; 02110 pc.idb.clear(); 02111 pc.edb = InterpretationPtr(new Interpretation(reg)); 02112 pc.currentOptimum.clear(); 02113 pc.config.setOption("NumberOfModels",0); 02114 pc.inputProvider = ip; 02115 ip.reset(); 02116 02117 // add facts F to the EDB of P 02118 pc.edb->getStorage() |= query.interpretation->getStorage(); 02119 02120 // compute all answer sets of P \cup F 02121 std::vector<InterpretationPtr> answersets = ctx.evaluateSubprogram(pc, true); 02122 02123 // learn support sets (only if --supportsets option is specified on the command line) 02124 if (learnSupportSets && !!nogoods && query.ctx->config.getOption("SupportSets")){ 02125 SimpleNogoodContainerPtr preparedNogoods = SimpleNogoodContainerPtr(new SimpleNogoodContainer()); 02126 02127 // for all rules r of P 02128 BOOST_FOREACH (ID ruleID, pc.idb){ 02129 const Rule& rule = reg->rules.getByID(ruleID); 02130 02131 // Check if r is a rule of form 02132 // hatom :- B, 02133 // where hatom is a single atom and B contains only positive atoms. 02134 bool posBody = true; 02135 BOOST_FOREACH (ID b, rule.body){ 02136 if (b.isNaf()) posBody = false; 02137 } 02138 if (rule.head.size() == 1 /*&& posBody*/){ 02139 // We learn the following (nonground) nogoods: { T b | b \in B } \cup { F hatom }. 02140 Nogood nogood; 02141 02142 // add all (positive) body atoms 02143 BOOST_FOREACH (ID blit, rule.body) nogood.insert(NogoodContainer::createLiteral(blit)); 02144 02145 // add the negated head atom 02146 nogood.insert(NogoodContainer::createLiteral(rule.head[0] | ID(ID::NAF_MASK, 0))); 02147 02148 // actually learn this nogood 02149 DBGLOG(DBG, "Learn prepared nogood " << nogood.getStringRepresentation(reg)); 02150 preparedNogoods->addNogood(nogood); 02151 } 02152 } 02153 02154 // exhaustively generate all resolvents of the prepared nogoods 02155 DBGLOG(DBG, "Computing resolvents of prepared nogoods up to size " << (query.interpretation->getStorage().count() + 1)); 02156 preparedNogoods->addAllResolvents(reg, query.interpretation->getStorage().count() + 1); 02157 02158 // all nogoods of form 02159 // { T b | b \in B } \cup { F q(X) } 02160 // containing only atoms over p and q are transformed into support sets of form 02161 // { T b | b \in B } \cup { F e_{&testCautiousQuery["prog", p, q]}(X) } 02162 // This is because if all body atoms are in the input (atoms over predicate p), then q(X) is true in every answer set of P \cup F. 02163 // But then, since q is the query predicate, also &testCautiousQuery["prog", p, q](X) is true. 02164 DBGLOG(DBG, "Extracting support sets from prepared nogoods"); 02165 for (int i = 0; i < preparedNogoods->getNogoodCount(); i++){ 02166 const Nogood& ng = preparedNogoods->getNogood(i); 02167 bool isSupportSet = true; 02168 Nogood supportSet; 02169 BOOST_FOREACH (ID id, ng){ 02170 ID pred = reg->lookupOrdinaryAtom(id).tuple[0]; 02171 if (pred == query.input[1]){ 02172 supportSet.insert(id); 02173 }else if (pred == query.input[2]){ 02174 const OrdinaryAtom& hatom = reg->lookupOrdinaryAtom(id); 02175 // add e_{&testCautiousQuery["prog", p, q]}(X) using a helper function 02176 supportSet.insert(NogoodContainer::createLiteral( 02177 ExternalLearningHelper::getOutputAtom( 02178 query, // this parameter is always the same 02179 Tuple(hatom.tuple.begin() + 1, hatom.tuple.end()), // hatom.tuple[0]=q and hatom.tuple[i] for i >= 1 stores the elements of X; 02180 // here we need only the X and use hatom.tuple.begin() + 1 to eliminate the predicate q 02181 !id.isNaf() /* technical detail, is set to true almost always */).address, 02182 true, // sign of the literal e_{&testCautiousQuery["prog", p, q]}(X) in the nogood 02183 id.isOrdinaryGroundAtom() /* specify if this literal is ground or nonground (the same as the head atom) */ )); 02184 }else{ 02185 isSupportSet = false; 02186 break; 02187 } 02188 } 02189 if (isSupportSet){ 02190 DBGLOG(DBG, "Learn support set: " << supportSet.getStringRepresentation(reg)); 02191 nogoods->addNogood(supportSet); 02192 } 02193 } 02194 } 02195 02196 // create a mask for the query predicate, i.e., retrieve all atoms over the query predicate 02197 PredicateMaskPtr pm = PredicateMaskPtr(new PredicateMask()); 02198 pm->setRegistry(reg); 02199 pm->addPredicate(query.input[2]); 02200 pm->updateMask(); 02201 02202 // now since we know all answer sets, we can answer the query 02203 answerQuery(pm, answersets, query, answer); 02204 } 02205 02206 // define an abstract method for aggregating the answer sets (this part is specific for cautious and brave queries) 02207 virtual void answerQuery(PredicateMaskPtr pm, std::vector<InterpretationPtr>& answersets, const Query& query, Answer& answer) = 0; 02208 }; 02209 02210 class TestCautiousQueryAtom: 02211 public TestASPQueryAtom 02212 { 02213 public: 02214 TestCautiousQueryAtom(ProgramCtx& ctx): 02215 TestASPQueryAtom(ctx, "testCautiousQuery") 02216 { } 02217 02218 // implement the specific part 02219 void answerQuery(PredicateMaskPtr pm, std::vector<InterpretationPtr>& answersets, const Query& query, Answer& answer){ 02220 02221 RegistryPtr reg = getRegistry(); 02222 02223 // special case: if there are no answer sets, cautious ground queries are trivially true, but cautious non-ground queries are always false for all ground substituions (by definition) 02224 if (answersets.size() == 0){ 02225 if (query.pattern.size() == 0){ 02226 // return the empty tuple 02227 Tuple t; 02228 answer.get().push_back(t); 02229 } 02230 }else{ 02231 02232 InterpretationPtr out = InterpretationPtr(new Interpretation(reg)); 02233 out->add(*pm->mask()); 02234 02235 // get the set of atoms over the query predicate which are true in all answer sets 02236 BOOST_FOREACH (InterpretationPtr intr, answersets){ 02237 out->getStorage() &= intr->getStorage(); 02238 } 02239 02240 // retrieve all output atoms oatom=q(c) 02241 bm::bvector<>::enumerator en = out->getStorage().first(); 02242 bm::bvector<>::enumerator en_end = out->getStorage().end(); 02243 while (en < en_end){ 02244 const OrdinaryAtom& oatom = reg->ogatoms.getByAddress(*en); 02245 02246 // add c to the output 02247 answer.get().push_back(Tuple(oatom.tuple.begin() + 1, oatom.tuple.end())); 02248 en++; 02249 } 02250 } 02251 } 02252 }; 02253 02254 class TestBraveQueryAtom: 02255 public TestASPQueryAtom 02256 { 02257 public: 02258 TestBraveQueryAtom(ProgramCtx& ctx): 02259 TestASPQueryAtom(ctx, "testCautiousBrave") 02260 { } 02261 02262 // implement the specific part 02263 virtual void answerQuery(PredicateMaskPtr pm, std::vector<InterpretationPtr>& answersets, const Query& query, Answer& answer){ 02264 02265 RegistryPtr reg = getRegistry(); 02266 02267 InterpretationPtr out = InterpretationPtr(new Interpretation(reg)); 02268 02269 // get the set of atoms over the query predicate which are true in all answer sets 02270 BOOST_FOREACH (InterpretationPtr intr, answersets){ 02271 out->getStorage() |= (pm->mask()->getStorage() & intr->getStorage()); 02272 } 02273 02274 // retrieve all output atoms oatom=q(c) 02275 bm::bvector<>::enumerator en = out->getStorage().first(); 02276 bm::bvector<>::enumerator en_end = out->getStorage().end(); 02277 while (en < en_end){ 02278 const OrdinaryAtom& oatom = reg->ogatoms.getByAddress(*en); 02279 02280 // add c to the output 02281 answer.get().push_back(Tuple(oatom.tuple.begin() + 1, oatom.tuple.end())); 02282 en++; 02283 } 02284 } 02285 }; 02286 02287 class TestFinalCallback: 02288 public FinalCallback 02289 { 02290 public: 02291 TestFinalCallback(ProgramCtx& ctx): 02292 ctx(ctx), 02293 first(true) 02294 { 02295 } 02296 02297 virtual void operator()() 02298 { 02299 std::cout << "TestFinalCallback::operator()()" << std::endl; 02300 if( first ) 02301 { 02302 // repeat 02303 ctx.config.setOption("RepeatEvaluation",1); 02304 } 02305 else 02306 { 02307 // don't repeat again 02308 } 02309 first = false; 02310 } 02311 02312 private: 02313 ProgramCtx& ctx; 02314 bool first; 02315 }; 02316 02317 class TestPlugin: 02318 public PluginInterface 02319 { 02320 public: 02321 struct CtxData: 02322 public PluginData 02323 { 02324 public: 02325 bool testRepetition; 02326 02327 public: 02328 CtxData(): 02329 testRepetition(false) {} 02330 virtual ~CtxData() {} 02331 }; 02332 02333 public: 02334 TestPlugin(): 02335 PluginInterface() 02336 { 02337 setNameVersion("dlvhex-testplugin", 0, 0, 1); 02338 } 02339 02340 virtual void processOptions(std::list<const char*>& pluginOptions, ProgramCtx& ctx) 02341 { 02342 TestPlugin::CtxData& pcd = ctx.getPluginData<TestPlugin>(); 02343 02344 typedef std::list<const char*>::iterator Iterator; 02345 Iterator it; 02346 WARNING("create (or reuse, maybe from potassco?) cmdline option processing facility") 02347 it = pluginOptions.begin(); 02348 while( it != pluginOptions.end() ) 02349 { 02350 bool processed = false; 02351 const std::string str(*it); 02352 if( str == "--testplugin-test-repetition" ) 02353 { 02354 pcd.testRepetition = true; 02355 std::cerr << "going to test repetition" << std::endl; 02356 processed = true; 02357 } 02358 02359 if( processed ) 02360 { 02361 it = pluginOptions.erase(it); 02362 } 02363 else 02364 { 02365 it++; 02366 } 02367 } 02368 } 02369 02370 class TestSetUnionAtom: // tests user-defined external learning 02371 public PluginAtom 02372 { 02373 02374 public: 02375 02376 TestSetUnionAtom(): 02377 // testSetUnion is the name of our external atom 02378 PluginAtom("testSetUnion", true) // monotonic 02379 { 02380 WARNING("TODO if a plugin atom has only constant inputs, is it always monotonic? if yes, automate this, at least create a warning") 02381 DBGLOG(DBG,"Constructor of SetUnion plugi is started!"); 02382 addInputPredicate(); // the first set 02383 addInputPredicate(); // the second set 02384 setOutputArity(1); // arity of the output list 02385 } 02386 02387 // function that evaluates external atom without learning 02388 // input parameters: 02389 // 1. Query is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Query) 02390 // 2. Answer is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Answer) 02391 virtual void retrieve(const Query& query, Answer& answer) 02392 { 02393 // find relevant input 02394 // Iterators (objects that mark the begin and the end of some structure) 02395 DBGLOG(DBG,"Retrieve function is started"); 02396 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 02397 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 02398 02399 std::vector<Tuple> tuples1; 02400 std::vector<Tuple> tuples2; 02401 // go through all atoms using the iterator 02402 while (en < en_end){ 02403 // extract the current atom 02404 // *emn is the id of the current atom, to which the iterator points 02405 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 02406 Tuple tu; 02407 // Iterate over the input elements of the current atom (for p(x,y), we go through x and y) 02408 // We start with 1 because the position 0 is the predicate itself 02409 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 02410 // Get element number i from the input list 02411 tu.push_back(atom.tuple[i]); 02412 } 02413 02414 if (atom.tuple[0] == query.input[0]){ 02415 tuples1.push_back(tu); 02416 } 02417 if (atom.tuple[0] == query.input[1]){ 02418 tuples2.push_back(tu); 02419 } 02420 en++; 02421 } 02422 02423 // for each element t of tuples1 add t to the answer 02424 BOOST_FOREACH (Tuple t, tuples1){ 02425 answer.get().push_back(t); 02426 } 02427 02428 BOOST_FOREACH (Tuple t, tuples2){ 02429 answer.get().push_back(t); 02430 } 02431 } 02432 02433 02434 // function that evaluates external atom with learning 02435 // input parameters: 02436 // 1. Query is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Query) 02437 // 2. Answer is a class, defined in PluginInterface.h (struct DLVHEX_EXPORT Answer) 02438 // 3. Learnt Nogoods 02439 02440 virtual void retrieve(const Query& query, Answer& answer, NogoodContainerPtr nogoods) 02441 { 02442 // find relevant input 02443 // Iterators (objects that mark the begin and the end of some structure) 02444 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 02445 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 02446 02447 std::vector<Tuple> tuples1; 02448 std::vector<Tuple> tuples2; 02449 // go through all atoms using the iterator 02450 while (en < en_end){ 02451 // extract the current atom 02452 // *emn is the id of the current atom, to which the iterator points 02453 const OrdinaryAtom& atom = getRegistry()->ogatoms.getByID(ID(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG, *en)); 02454 Tuple tu; 02455 // Iterate over the input elements of the current atom (for p(x,y), we go through x and y) 02456 // We start with 1 because the position 0 is the predicate itself 02457 for (uint32_t i = 1; i < atom.tuple.size(); ++i){ 02458 // Get element number i from the input list 02459 tu.push_back(atom.tuple[i]); 02460 } 02461 02462 if (atom.tuple[0] == query.input[0]){ 02463 tuples1.push_back(tu); 02464 } 02465 if (atom.tuple[0] == query.input[1]){ 02466 tuples2.push_back(tu); 02467 } 02468 en++; 02469 } 02470 02471 // for each element t of tuples1 add t to the answer 02472 BOOST_FOREACH (Tuple t, tuples1){ 02473 answer.get().push_back(t); 02474 // G in the end stands for ground learning (N for nonground) 02475 // Create a new object where we store the copy of the first input predictae 02476 OrdinaryAtom at1(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYG); 02477 // Copy input predicate with the parameters to at1 02478 at1.tuple.push_back(query.input[0]); 02479 // arity is always 1 here 02480 BOOST_FOREACH (ID i, t) { 02481 at1.tuple.push_back(i); 02482 } 02483 // Start with empty nogood 02484 Nogood nogood; 02485 // Add the first literal 02486 // In case of a nonground nogood, we need to store NAtom (storeOrdinaryNAtom) 02487 // First true is the sign of the literal 02488 // Second parameter is true if we create ground nogood 02489 02490 nogood.insert(NogoodContainer::createLiteral(getRegistry()->storeOrdinaryGAtom(at1).address, true, true)); 02491 02492 // ExternalLearningHelper is a function that helps to create an element in a nogood for external atom: call the function for the given output tuple 02493 // Always the same (add the false output in case if under the input parameters the result is true) 02494 nogood.insert(NogoodContainer::createLiteral(ExternalLearningHelper::getOutputAtom(query, t, false).address, true, false)); 02495 // add the nogood to the set of all nogoods if nogoods is not zero 02496 if (!!nogoods) 02497 nogoods->addNogood(nogood); 02498 DBGLOG(DBG,"nogood is " << nogood); 02499 02500 } 02501 02502 BOOST_FOREACH (Tuple t, tuples2){ 02503 answer.get().push_back(t); 02504 } 02505 } 02506 }; 02507 02508 class TestGen2Atom: // tests user-defined external learning 02509 public PluginAtom 02510 { 02511 public: 02512 TestGen2Atom(std::string name, int arity): 02513 PluginAtom(name, false) 02514 { 02515 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 02516 addInputPredicate(); 02517 for (int i = 0; i < arity; ++i) addInputConstant(); 02518 setOutputArity(0); 02519 } 02520 02521 virtual void retrieve(const Query& query, Answer& answer) 02522 { 02523 OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 02524 myat.tuple = query.input; 02525 02526 // find relevant input 02527 bool match = false; 02528 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 02529 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 02530 while (en < en_end){ 02531 02532 if (getRegistry()->ogatoms.getByAddress(*en).unifiesWith(myat)){ 02533 match = true; 02534 break; 02535 } 02536 en++; 02537 } 02538 Tuple tu; 02539 if (match) answer.get().push_back(tu); 02540 } 02541 }; 02542 02543 class TestIsEmpty: // tests user-defined external learning 02544 public PluginAtom 02545 { 02546 public: 02547 TestIsEmpty(): 02548 PluginAtom("testIsEmpty", false) 02549 { 02550 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 02551 addInputPredicate(); 02552 setOutputArity(0); 02553 } 02554 02555 virtual void retrieve(const Query& query, Answer& answer) 02556 { 02557 OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 02558 myat.tuple = query.input; 02559 02560 // find relevant input 02561 bm::bvector<>::enumerator en = query.interpretation->getStorage().first(); 02562 bm::bvector<>::enumerator en_end = query.interpretation->getStorage().end(); 02563 while (en < en_end){ 02564 return; // not empty 02565 } 02566 02567 // empty 02568 Tuple tu; 02569 answer.get().push_back(tu); 02570 } 02571 }; 02572 02573 class TestNumberOfBalls: // tests user-defined external learning 02574 public PluginAtom 02575 { 02576 public: 02577 TestNumberOfBalls(): 02578 PluginAtom("testNumberOfBalls", false) 02579 { 02580 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 02581 addInputPredicate(); 02582 addInputConstant(); 02583 addInputConstant(); 02584 setOutputArity(0); 02585 02586 prop.providesPartialAnswer = true; 02587 } 02588 02589 virtual void retrieve(const Query& query, Answer& answer) 02590 { 02591 OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 02592 myat.tuple = query.input; 02593 02594 // find relevant input 02595 int tr = 0; 02596 int fa = 0; 02597 int un = 0; 02598 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first(); 02599 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end(); 02600 while (en < en_end){ 02601 if ((!query.assigned || query.assigned->getFact(*en)) && query.interpretation->getFact(*en)) tr++; 02602 else if ((!query.assigned || query.assigned->getFact(*en)) && !query.interpretation->getFact(*en)) fa++; 02603 else un++; 02604 en++; 02605 } 02606 02607 if (tr >= query.input[1].address && (tr + un) <= query.input[2].address){ 02608 // true 02609 Tuple tu; 02610 answer.get().push_back(tu); 02611 }else if ((tr + un) >= query.input[1].address && tr <= query.input[2].address){ 02612 // unknwon 02613 Tuple tu; 02614 answer.getUnknown().push_back(tu); 02615 }else{ 02616 // false 02617 } 02618 } 02619 }; 02620 02621 class TestNumberOfBallsSE: // tests user-defined external learning 02622 public PluginAtom 02623 { 02624 public: 02625 TestNumberOfBallsSE(): 02626 PluginAtom("testNumberOfBallsSE", false) 02627 { 02628 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 02629 addInputPredicate(); 02630 addInputConstant(); 02631 setOutputArity(0); 02632 02633 prop.providesPartialAnswer = true; 02634 prop.antimonotonicInputPredicates.insert(0); 02635 } 02636 02637 virtual void retrieve(const Query& query, Answer& answer) 02638 { 02639 OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 02640 myat.tuple = query.input; 02641 02642 // find relevant input 02643 int tr = 0; 02644 int fa = 0; 02645 int un = 0; 02646 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first(); 02647 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end(); 02648 while (en < en_end){ 02649 if ((!query.assigned || query.assigned->getFact(*en)) && query.interpretation->getFact(*en)) tr++; 02650 else if ((!query.assigned || query.assigned->getFact(*en)) && !query.interpretation->getFact(*en)) fa++; 02651 else un++; 02652 en++; 02653 } 02654 02655 if ((tr + un) <= query.input[1].address){ 02656 // true 02657 Tuple tu; 02658 answer.get().push_back(tu); 02659 }else if (tr <= query.input[1].address){ 02660 // unknwon 02661 Tuple tu; 02662 answer.getUnknown().push_back(tu); 02663 }else{ 02664 // false 02665 } 02666 } 02667 }; 02668 02669 class TestNumberOfBallsGE: // tests user-defined external learning 02670 public PluginAtom 02671 { 02672 public: 02673 TestNumberOfBallsGE(): 02674 PluginAtom("testNumberOfBallsGE", false) 02675 { 02676 WARNING("TODO if a plugin atom has only onstant inputs, is it always monotonic? if yes, automate this, at least create a warning") 02677 addInputPredicate(); 02678 addInputConstant(); 02679 setOutputArity(0); 02680 02681 prop.providesPartialAnswer = true; 02682 prop.monotonicInputPredicates.insert(0); 02683 } 02684 02685 virtual void retrieve(const Query& query, Answer& answer) 02686 { 02687 OrdinaryAtom myat(ID::MAINKIND_ATOM | ID::SUBKIND_ATOM_ORDINARYN); 02688 myat.tuple = query.input; 02689 02690 // find relevant input 02691 int tr = 0; 02692 int fa = 0; 02693 int un = 0; 02694 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first(); 02695 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end(); 02696 while (en < en_end){ 02697 if ((!query.assigned || query.assigned->getFact(*en)) && query.interpretation->getFact(*en)) tr++; 02698 else if ((!query.assigned || query.assigned->getFact(*en)) && !query.interpretation->getFact(*en)) fa++; 02699 else un++; 02700 en++; 02701 } 02702 02703 if (tr >= query.input[1].address) { 02704 // true 02705 Tuple tu; 02706 answer.get().push_back(tu); 02707 }else if ((tr + un) >= query.input[1].address) { 02708 // unknwon 02709 Tuple tu; 02710 answer.getUnknown().push_back(tu); 02711 }else{ 02712 // false 02713 } 02714 } 02715 }; 02716 02717 02718 class SumNonZeroAtom : public PluginAtom 02719 { 02720 public: 02721 SumNonZeroAtom() : PluginAtom("sumD0", 1) 02722 { 02723 addInputPredicate(); 02724 setOutputArity(1); 02725 } 02726 02727 virtual void 02728 retrieve(const Query& query, Answer& answer) throw (PluginError) 02729 { 02730 Registry ®istry = *getRegistry(); 02731 02732 int sum = 0; 02733 int pos = query.interpretation.get()->getStorage().get_first(); 02734 while (pos != 0){ 02735 const OrdinaryAtom& oatom = registry.ogatoms.getByAddress(pos); 02736 if(oatom.tuple[1].address == 0) 02737 sum += oatom.tuple[2].address; 02738 else 02739 sum -= oatom.tuple[2].address; 02740 pos = query.interpretation.get()->getStorage().get_next(pos); 02741 } 02742 02743 Tuple out; 02744 out.push_back(ID::termFromInteger(sum == 0 ? 0 : 1)); 02745 answer.get().push_back(out); 02746 } 02747 }; 02748 02749 class ProductionRequirementsAtom : public PluginAtom 02750 { 02751 public: 02752 ProductionRequirementsAtom() : PluginAtom("getreq", false) 02753 { 02754 addInputPredicate(); 02755 addInputPredicate(); 02756 setOutputArity(1); 02757 02758 prop.setProvidesPartialAnswer(true); 02759 } 02760 02761 virtual void 02762 retrieve(const Query& query, Answer& answer) throw (PluginError) 02763 { 02764 Registry ®istry = *getRegistry(); 02765 02766 std::set<ID> produced; 02767 std::set<ID> possiblyproduced; 02768 std::set<ID> allrequirements; 02769 02770 ID const_p = registry.storeConstantTerm("p"); 02771 ID const_n = registry.storeConstantTerm("n"); 02772 02773 // extract requirements and production plan 02774 bm::bvector<>::enumerator en = query.predicateInputMask->getStorage().first(); 02775 bm::bvector<>::enumerator en_end = query.predicateInputMask->getStorage().end(); 02776 while (en < en_end){ 02777 ID id = registry.ogatoms.getIDByAddress(*en); 02778 const OrdinaryAtom& ogatom = registry.ogatoms.getByAddress(*en); 02779 if (ogatom.tuple[0] == query.input[0]){ 02780 if (!query.assigned || query.assigned->getFact(*en)){ 02781 if (query.interpretation->getFact(*en)) { produced.insert(ogatom.tuple[1]); } 02782 } else { possiblyproduced.insert(ogatom.tuple[1]); } 02783 } 02784 if (ogatom.tuple[0] == query.input[1]){ 02785 allrequirements.insert(ogatom.tuple[1]); 02786 } 02787 en++; 02788 } 02789 02790 // decide for each requirement if it is true, false or unknown 02791 BOOST_FOREACH (ID req, allrequirements) { 02792 en = query.predicateInputMask->getStorage().first(); 02793 en_end = query.predicateInputMask->getStorage().end(); 02794 while (en < en_end){ 02795 const OrdinaryAtom& ogatom = registry.ogatoms.getByAddress(*en); 02796 if (ogatom.tuple[0] == query.input[1] && ogatom.tuple[1] == req){ 02797 if (ogatom.tuple[2] != const_p) throw PluginError("requirements specification must be of form req(Name, p, ..., n, ...)"); 02798 bool cursat = true; 02799 bool curviolated = false; 02800 bool pos = true; 02801 for (int i = 3; i < ogatom.tuple.size(); ++i) { 02802 // switch to negative requirements 02803 if (ogatom.tuple[i] == const_n) { 02804 pos = false; 02805 continue; 02806 } 02807 // check for satisfaction of the current product 02808 cursat &= (pos && produced.find(ogatom.tuple[i]) != produced.end()) || (!pos && produced.find(ogatom.tuple[i]) == produced.end() && possiblyproduced.find(ogatom.tuple[i]) == possiblyproduced.end()); 02809 curviolated |= (pos && produced.find(ogatom.tuple[i]) == produced.end() && possiblyproduced.find(ogatom.tuple[i]) == possiblyproduced.end()) || (!pos && produced.find(ogatom.tuple[i]) != produced.end()); 02810 } 02811 assert (!(cursat && curviolated) && "precondition for requirement is satisfied and violated at the same time"); 02812 // requirement is definitely true 02813 if (cursat) { 02814 Tuple out; 02815 out.push_back(req); 02816 answer.get().push_back(out); 02817 break; 02818 } 02819 // requirement could be true 02820 if (!curviolated) { 02821 Tuple out; 02822 out.push_back(req); 02823 answer.getUnknown().push_back(out); 02824 break; 02825 } 02826 } 02827 en++; 02828 } 02829 } 02830 } 02831 }; 02832 02833 virtual std::vector<PluginAtomPtr> createAtoms(ProgramCtx& ctx) const 02834 { 02835 std::vector<PluginAtomPtr> ret; 02836 02837 // return smart pointer with deleter (i.e., delete code compiled into this plugin) 02838 ret.push_back(PluginAtomPtr(new TestAAtom, PluginPtrDeleter<PluginAtom>())); 02839 ret.push_back(PluginAtomPtr(new TestBAtom, PluginPtrDeleter<PluginAtom>())); 02840 ret.push_back(PluginAtomPtr(new TestCAtom, PluginPtrDeleter<PluginAtom>())); 02841 ret.push_back(PluginAtomPtr(new TestZeroArityAtom("testZeroArity0", false), PluginPtrDeleter<PluginAtom>())); 02842 ret.push_back(PluginAtomPtr(new TestZeroArityAtom("testZeroArity1", true), PluginPtrDeleter<PluginAtom>())); 02843 ret.push_back(PluginAtomPtr(new TestConcatAtom, PluginPtrDeleter<PluginAtom>())); 02844 ret.push_back(PluginAtomPtr(new TestConcatAllAtom, PluginPtrDeleter<PluginAtom>())); 02845 ret.push_back(PluginAtomPtr(new TestListDomainAtom, PluginPtrDeleter<PluginAtom>())); 02846 ret.push_back(PluginAtomPtr(new TestListConcatAtom, PluginPtrDeleter<PluginAtom>())); 02847 ret.push_back(PluginAtomPtr(new TestListLengthAtom, PluginPtrDeleter<PluginAtom>())); 02848 ret.push_back(PluginAtomPtr(new TestListSplitAtom, PluginPtrDeleter<PluginAtom>())); 02849 ret.push_back(PluginAtomPtr(new TestListSplitHalfAtom, PluginPtrDeleter<PluginAtom>())); 02850 ret.push_back(PluginAtomPtr(new TestListMergeAtom, PluginPtrDeleter<PluginAtom>())); 02851 ret.push_back(PluginAtomPtr(new TestSubstrAtom, PluginPtrDeleter<PluginAtom>())); 02852 ret.push_back(PluginAtomPtr(new TestSmallerThanAtom, PluginPtrDeleter<PluginAtom>())); 02853 ret.push_back(PluginAtomPtr(new TestFirstAtom, PluginPtrDeleter<PluginAtom>())); 02854 ret.push_back(PluginAtomPtr(new TestPushAtom, PluginPtrDeleter<PluginAtom>())); 02855 ret.push_back(PluginAtomPtr(new TestMoveAtom, PluginPtrDeleter<PluginAtom>())); 02856 ret.push_back(PluginAtomPtr(new TestStrlenAtom, PluginPtrDeleter<PluginAtom>())); 02857 ret.push_back(PluginAtomPtr(new TestSetMinusAtom, PluginPtrDeleter<PluginAtom>())); 02858 ret.push_back(PluginAtomPtr(new TestSetMinusNogoodBasedLearningAtom, PluginPtrDeleter<PluginAtom>())); 02859 ret.push_back(PluginAtomPtr(new TestSetMinusNonComfortAtom, PluginPtrDeleter<PluginAtom>())); 02860 ret.push_back(PluginAtomPtr(new TestSetMinusPartialAtom, PluginPtrDeleter<PluginAtom>())); 02861 ret.push_back(PluginAtomPtr(new TestSetMinusNongroundNogoodBasedLearningAtom, PluginPtrDeleter<PluginAtom>())); 02862 ret.push_back(PluginAtomPtr(new TestSetMinusRuleBasedLearningAtom(&ctx), PluginPtrDeleter<PluginAtom>())); 02863 ret.push_back(PluginAtomPtr(new TestSetUnionAtom, PluginPtrDeleter<PluginAtom>())); 02864 ret.push_back(PluginAtomPtr(new TestNonmonAtom, PluginPtrDeleter<PluginAtom>())); 02865 ret.push_back(PluginAtomPtr(new TestNonmon2Atom, PluginPtrDeleter<PluginAtom>())); 02866 ret.push_back(PluginAtomPtr(new TestIdAtom, PluginPtrDeleter<PluginAtom>())); 02867 ret.push_back(PluginAtomPtr(new TestIdpAtom, PluginPtrDeleter<PluginAtom>())); 02868 ret.push_back(PluginAtomPtr(new TestIdcAtom, PluginPtrDeleter<PluginAtom>())); 02869 ret.push_back(PluginAtomPtr(new TestNegAtom, PluginPtrDeleter<PluginAtom>())); 02870 ret.push_back(PluginAtomPtr(new TestMinusOneAtom, PluginPtrDeleter<PluginAtom>())); 02871 ret.push_back(PluginAtomPtr(new TestEvenAtom, PluginPtrDeleter<PluginAtom>())); 02872 ret.push_back(PluginAtomPtr(new TestOddAtom, PluginPtrDeleter<PluginAtom>())); 02873 ret.push_back(PluginAtomPtr(new TestLessThanAtom, PluginPtrDeleter<PluginAtom>())); 02874 ret.push_back(PluginAtomPtr(new TestEqualAtom, PluginPtrDeleter<PluginAtom>())); 02875 ret.push_back(PluginAtomPtr(new TestTransitiveClosureAtom, PluginPtrDeleter<PluginAtom>())); 02876 ret.push_back(PluginAtomPtr(new TestCycleAtom, PluginPtrDeleter<PluginAtom>())); 02877 ret.push_back(PluginAtomPtr(new TestAppendAtom, PluginPtrDeleter<PluginAtom>())); 02878 ret.push_back(PluginAtomPtr(new TestDisjAtom, PluginPtrDeleter<PluginAtom>())); 02879 ret.push_back(PluginAtomPtr(new TestHashAtom, PluginPtrDeleter<PluginAtom>())); 02880 ret.push_back(PluginAtomPtr(new TestTrueMultiInpAtom, PluginPtrDeleter<PluginAtom>())); 02881 ret.push_back(PluginAtomPtr(new TestTrueMultiInpAtom2, PluginPtrDeleter<PluginAtom>())); 02882 ret.push_back(PluginAtomPtr(new TestReachableAtom, PluginPtrDeleter<PluginAtom>())); 02883 ret.push_back(PluginAtomPtr(new TestDLSimulatorAtom, PluginPtrDeleter<PluginAtom>())); 02884 ret.push_back(PluginAtomPtr(new TestCautiousQueryAtom(ctx), PluginPtrDeleter<PluginAtom>())); 02885 ret.push_back(PluginAtomPtr(new TestBraveQueryAtom(ctx), PluginPtrDeleter<PluginAtom>())); 02886 ret.push_back(PluginAtomPtr(new TestGen2Atom("gen1", 1), PluginPtrDeleter<PluginAtom>())); 02887 ret.push_back(PluginAtomPtr(new TestGen2Atom("gen2", 2), PluginPtrDeleter<PluginAtom>())); 02888 ret.push_back(PluginAtomPtr(new TestGen2Atom("gen3", 3), PluginPtrDeleter<PluginAtom>())); 02889 ret.push_back(PluginAtomPtr(new TestIsEmpty, PluginPtrDeleter<PluginAtom>())); 02890 ret.push_back(PluginAtomPtr(new TestNumberOfBalls, PluginPtrDeleter<PluginAtom>())); 02891 ret.push_back(PluginAtomPtr(new TestNumberOfBallsSE, PluginPtrDeleter<PluginAtom>())); 02892 ret.push_back(PluginAtomPtr(new TestNumberOfBallsGE, PluginPtrDeleter<PluginAtom>())); 02893 ret.push_back(PluginAtomPtr(new SumNonZeroAtom, PluginPtrDeleter<PluginAtom>())); 02894 ret.push_back(PluginAtomPtr(new ProductionRequirementsAtom, PluginPtrDeleter<PluginAtom>())); 02895 02896 return ret; 02897 } 02898 02899 02900 virtual void setupProgramCtx(ProgramCtx& ctx) 02901 { 02902 TestPlugin::CtxData& pcd = ctx.getPluginData<TestPlugin>(); 02903 02904 if( pcd.testRepetition ) 02905 { 02906 ctx.finalCallbacks.push_back( 02907 FinalCallbackPtr(new TestFinalCallback(ctx))); 02908 } 02909 } 02910 }; 02911 02912 TestPlugin theTestPlugin; 02913 02914 DLVHEX_NAMESPACE_END 02915 02916 IMPLEMENT_PLUGINABIVERSIONFUNCTION 02917 02918 // return plain C type s.t. all compilers and linkers will like this code 02919 extern "C" 02920 DLVHEX_PLUGINEXPORT 02921 void * PLUGINIMPORTFUNCTION() 02922 { 02923 return reinterpret_cast<void*>(& DLVHEX_NAMESPACE theTestPlugin); 02924 } 02925 02926 /* vim: set noet sw=2 ts=2 tw=80: */