00001 #include <actasp/reasoners/Clingo.h>
00002
00003 #include "IsNotLocallyOptimal.h"
00004 #include "LexComparator.h"
00005
00006 #include <actasp/AnswerSet.h>
00007 #include <actasp/action_utils.h>
00008 #include <actasp/Action.h>
00009
00010 #include <ctime>
00011
00012 #include <sstream>
00013 #include <ostream>
00014 #include <iterator>
00015 #include <algorithm>
00016 #include <fstream>
00017 #include <cmath>
00018
00019 #include <iostream>
00020 #include <ctime>
00021
00022 #define CURRENT_FILE_HOME std::string("/tmp/")
00023 #define CURRENT_STATE_FILE std::string("current.asp")
00024
00025 using namespace std;
00026
00027 namespace actasp {
00028
00029
00030
00031
00032 Clingo::Clingo(unsigned int max_n,
00033 const std::string& incrementalVar,
00034 const std::string& queryDir,
00035 const std::string& domainDir,
00036 const ActionSet& actions,
00037 unsigned int max_time
00038 ) throw() :
00039 max_n(max_n),
00040 incrementalVar(incrementalVar),
00041 max_time(max_time),
00042 queryDir(queryDir),
00043 domainDir(domainDir),
00044 allActions(actions),
00045 actionFilter() {
00046
00047 if (max_time > 0 && !system("timeout 2>/dev/null"))
00048 max_time = 0;
00049
00050
00051
00052 if (this->queryDir.find_last_of("/") != (this->queryDir.length() -1))
00053 this->queryDir += "/";
00054
00055 if ((this->domainDir.find_last_of("/")) != (this->domainDir.length() -1))
00056 this->domainDir += "/";
00057
00058
00059
00060 stringstream filterStream;
00061 filterStream << "#hide." << endl;
00062
00063 std::set<AspFluent>::const_iterator actIt = allActions.begin();
00064 for (; actIt != allActions.end(); ++actIt) {
00065 filterStream << "#show " << actIt->getName() << "/" << actIt->arity() << "." << endl;
00066 }
00067
00068 actionFilter = filterStream.str();
00069 }
00070
00071 struct RuleToString {
00072 RuleToString(unsigned int timeStepNum) {
00073 stringstream ss;
00074 ss << timeStepNum;
00075 timeStep = ss.str();
00076 }
00077
00078 RuleToString(const string& timeStepVar) : timeStep(timeStepVar) {}
00079
00080 std::string operator()(const AspRule& rule) {
00081
00082 stringstream ruleStream;
00083
00084
00085 for (int i =0, size = rule.head.size(); i <size; ++i) {
00086 ruleStream << rule.head[i].toString(timeStep);
00087 if (i < (size-1))
00088 ruleStream << ", ";
00089 }
00090
00091 if (!(rule.body.empty()))
00092 ruleStream << ":- ";
00093
00094
00095 for (int i =0, size = rule.body.size(); i <size; ++i) {
00096 ruleStream << rule.body[i].toString(timeStep);
00097 if (i < (size-1))
00098 ruleStream << ", ";
00099 }
00100
00101 if (!(rule.head.empty() && rule.body.empty()))
00102 ruleStream << "." << std::endl;
00103
00104 return ruleStream.str();
00105 }
00106
00107 string timeStep;
00108 };
00109
00110
00111 static string aspString(const std::vector<actasp::AspRule>& query, const string& timeStepVar) {
00112
00113 stringstream aspStream;
00114 transform(query.begin(),query.end(),ostream_iterator<std::string>(aspStream),RuleToString(timeStepVar));
00115 return aspStream.str();
00116 }
00117
00118 static string aspString(const std::vector<actasp::AspRule>& query, unsigned int timeStep) {
00119
00120 stringstream vs;
00121 vs << timeStep;
00122
00123 return aspString(query,vs.str());
00124 }
00125
00126 static std::list<AspFluent> parseAnswerSet(const std::string& answerSetContent) {
00127
00128 stringstream predicateLine(answerSetContent);
00129
00130 list<AspFluent> predicates;
00131
00132
00133 copy(istream_iterator<string>(predicateLine),
00134 istream_iterator<string>(),
00135 back_inserter(predicates));
00136
00137 return predicates;
00138 }
00139
00140
00141 static std::list<actasp::AnswerSet> readAnswerSets(const std::string& filePath) {
00142
00143 ifstream file(filePath.c_str());
00144
00145 list<AnswerSet> allSets;
00146 bool answerFound = false;
00147
00148 string line;
00149 while(file) {
00150
00151 getline(file,line);
00152
00153 if(answerFound && line == "UNSATISFIABLE")
00154 return list<AnswerSet>();
00155
00156 if(line.find("Answer") != string::npos) {
00157 getline(file,line);
00158 try {
00159 list<AspFluent> fluents = parseAnswerSet(line);
00160 allSets.push_back(AnswerSet(fluents.begin(), fluents.end()));
00161 } catch (std::invalid_argument& arg) {
00162
00163 }
00164 }
00165 }
00166
00167 return allSets;
00168 }
00169
00170 std::list<actasp::AnswerSet> Clingo::krQuery(const std::string& query,
00171 unsigned int initialTimeStep,
00172 unsigned int finalTimeStep,
00173 const std::string& fileName,
00174 unsigned int answerSetsNumber = 1) const throw() {
00175
00176
00177
00178
00179 initialTimeStep++;
00180 finalTimeStep++;
00181
00182 string queryPath = queryDir + fileName;
00183
00184 ofstream queryFile(queryPath.c_str());
00185 queryFile << query << endl;
00186 queryFile.close();
00187
00188 stringstream commandLine;
00189
00190 const string outputFilePath = queryDir + "query_output.txt";
00191
00192 if (max_time > 0) {
00193 commandLine << "timeout " << max_time << " ";
00194 }
00195
00196 stringstream iterations;
00197 iterations << "--imin=" << initialTimeStep << " --imax=" << finalTimeStep;
00198
00199 commandLine << "iclingo " << iterations.str() << " " << queryPath << " " << domainDir << "*.asp " << (CURRENT_FILE_HOME + CURRENT_STATE_FILE) << " > " << outputFilePath << " " << answerSetsNumber;
00200
00201
00202 if (!system(commandLine.str().c_str())) {
00203
00204 }
00205
00206 return readAnswerSets(outputFilePath);
00207
00208 }
00209
00210 string Clingo::generatePlanQuery(const std::vector<actasp::AspRule>& goalRules,
00211 bool filterActions = true) const throw() {
00212 stringstream goal;
00213 goal << "#volatile " << incrementalVar << "." << endl;
00214
00215 goal << aspString(goalRules,incrementalVar+"-1") << endl;
00216
00217 if (filterActions)
00218 goal << actionFilter;
00219
00220
00221 return goal.str();
00222 }
00223
00224
00225
00226 ActionSet Clingo::availableActions() const throw() {
00227 list<AnswerSet> actions = krQuery(generatePlanQuery(vector<AspRule>(), true),1,1,"planQuery.asp",0);
00228
00229 ActionSet computed;
00230
00231
00232 list<AnswerSet>::iterator act = ++actions.begin();
00233 for (; act != actions.end(); ++act) {
00234 computed.insert(act->getFluents().begin(), act->getFluents().end());
00235 }
00236
00237 return computed;
00238 }
00239
00240
00241 AnswerSet Clingo::computePlan(const std::vector<actasp::AspRule>& goal) const throw() {
00242
00243 list<AnswerSet> answerSets;
00244
00245 string query = generatePlanQuery(goal);
00246
00247 answerSets = krQuery(query,0,max_n,"planQuery.asp");
00248
00249
00250 if (answerSets.empty())
00251 return AnswerSet();
00252
00253 return *(answerSets.begin());
00254 }
00255
00256 struct PolicyMerger {
00257
00258 PolicyMerger(MultiPolicy & policy) :
00259 policy(policy) {}
00260
00261 void operator()(const AnswerSet& set) {
00262 policy.merge(set);
00263 }
00264
00265 MultiPolicy &policy;
00266 };
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326 struct IsSubSequence {
00327
00328 IsSubSequence(const list< AspFluent> &myPlan) : longerPlan(myPlan) {};
00329
00330 bool operator()(const list<AspFluent> &shorterPlan) const {
00331
00332 pair< list< AspFluent>::const_iterator, list< AspFluent>::const_iterator>
00333 start = mismatch(shorterPlan.begin(),shorterPlan.end(),longerPlan.begin(), ActionEquality());
00334
00335 size_t remaining = distance(start.first, shorterPlan.end());
00336 list< AspFluent>::const_iterator secondStart = longerPlan.end();
00337 advance(secondStart, -remaining);
00338
00339 return equal(start.first, shorterPlan.end(), secondStart, ActionEquality());
00340
00341 }
00342
00343 const list< AspFluent> &longerPlan;
00344
00345 };
00346
00347 struct IsNotLocallyOptimalSubPlanCheck {
00348
00349 IsNotLocallyOptimalSubPlanCheck(const set< list< AspFluent>,LexComparator > &allPlans, const ActionSet &allActions) :
00350 allPlans(allPlans), allActions(allActions) {}
00351
00352 bool operator()(const AnswerSet& plan) const {
00353 list<AspFluent> actionsOnly;
00354
00355 remove_copy_if(plan.getFluents().begin(), plan.getFluents().end(),
00356 back_inserter(actionsOnly),not1(IsAnAction(allActions)));
00357
00358 set< list< AspFluent>, LexComparator >::const_iterator sub = find_if(allPlans.begin(),allPlans.end(),IsSubSequence(actionsOnly));
00359
00360 return sub != allPlans.end();
00361 }
00362
00363 const set< list< AspFluent>, LexComparator > &allPlans;
00364 const ActionSet &allActions;
00365 };
00366
00367 struct CleanPlan {
00368
00369 CleanPlan(const ActionSet &allActions) : allActions(allActions) {}
00370
00371 list<AspFluentRef> operator()(const AnswerSet &planWithStates) const {
00372 list<AspFluentRef> actionsOnly;
00373
00374 remove_copy_if(planWithStates.getFluents().begin(), planWithStates.getFluents().end(),
00375 back_inserter(actionsOnly),not1(IsAnAction(allActions)));
00376
00377 return actionsOnly;
00378
00379 }
00380
00381 const ActionSet &allActions;
00382
00383 };
00384
00385 struct PlanLongerThan {
00386
00387 PlanLongerThan(unsigned int length) : length(length) {}
00388
00389 bool operator()(const AnswerSet& plan) const {
00390 return plan.maxTimeStep() > length;
00391 }
00392
00393 unsigned int length;
00394 };
00395
00396 struct AnswerSetRef {
00397 AnswerSetRef(const AnswerSet& aset) : aset(&aset) {}
00398
00399 operator const AnswerSet&() const {
00400 return *aset;
00401 }
00402
00403 const AnswerSet *aset;
00404 };
00405
00406
00407
00408 MultiPolicy Clingo::computePolicy(const std::vector<actasp::AspRule>& goal, double suboptimality) const throw (std::logic_error) {
00409
00410 if (suboptimality < 1) {
00411 stringstream num;
00412 num << suboptimality;
00413 throw logic_error("Clingo: suboptimality value cannot be less then one, found: " + num.str());
00414 }
00415
00416 string query = generatePlanQuery(goal,false);
00417
00418 clock_t kr1_begin = clock();
00419 list<AnswerSet> firstAnswerSets = krQuery(query,1,max_n,"planQuery.asp",0);
00420 clock_t kr1_end = clock();
00421 cout << "The first kr call took " << (double(kr1_end - kr1_begin) / CLOCKS_PER_SEC) << " seconds" << endl;
00422
00423 MultiPolicy policy(allActions);
00424
00425 if (firstAnswerSets.empty())
00426 return policy;
00427
00428 unsigned int shortestLength = firstAnswerSets.begin()->maxTimeStep();
00429
00430 for_each(firstAnswerSets.begin(),firstAnswerSets.end(),PolicyMerger(policy));
00431
00432 int maxLength = floor(suboptimality * shortestLength);
00433
00434 if (maxLength == shortestLength)
00435 return policy;
00436
00437
00438 set< list <AspFluentRef>, LexComparator > goodPlans;
00439
00440
00441 transform(firstAnswerSets.begin(),firstAnswerSets.end(),inserter(goodPlans,goodPlans.begin()), CleanPlan(allActions));
00442
00443 query = generatePlanQuery(goal,false);
00444
00445 clock_t kr2_begin = clock();
00446 list<AnswerSet> answerSets = krQuery(query,maxLength,maxLength,"planQuery.asp",0);
00447 clock_t kr2_end = clock();
00448 cout << "The second kr call took " << (double(kr2_end - kr2_begin) / CLOCKS_PER_SEC) << " seconds" << endl;
00449
00450
00451 list<AnswerSet>::iterator currentFirst = find_if(answerSets.begin(),answerSets.end(),PlanLongerThan(answerSets.begin()->maxTimeStep()));
00452
00453 set< list <AspFluentRef>, LexComparator > badPlans;
00454
00455 IsNotLocallyOptimal isNotLocallyOptimal(&goodPlans,&badPlans, allActions, shortestLength,false);
00456
00457 clock_t filter_begin = clock();
00458 while (currentFirst != answerSets.end()) {
00459
00460
00461
00462 list<AnswerSet>::iterator currentLast = find_if(currentFirst,answerSets.end(),PlanLongerThan(currentFirst->maxTimeStep()));
00463
00464 list<AnswerSetRef> goodPointers;
00465 remove_copy_if(currentFirst,currentLast,back_inserter(goodPointers),isNotLocallyOptimal);
00466
00467 for_each(goodPointers.begin(),goodPointers.end(),PolicyMerger(policy));
00468
00469 transform(goodPointers.begin(),goodPointers.end(),inserter(goodPlans, goodPlans.begin()), CleanPlan(allActions));
00470
00471 currentFirst = currentLast;
00472
00473 }
00474 clock_t filter_end = clock();
00475
00476 set< list <AspFluentRef>, LexComparator >::const_iterator printIt = goodPlans.begin();
00477 for (; printIt != goodPlans.end(); ++printIt) {
00478 copy(printIt->begin(),printIt->end(),ostream_iterator<string>(cout, " "));
00479 cout << endl;
00480 }
00481
00482 cout << "filtering took " << (double(filter_end - filter_begin) / CLOCKS_PER_SEC) << " seconds" << endl;
00483
00484
00485 return policy;
00486
00487 }
00488
00489 struct AnswerSetToList {
00490 list <AspFluentRef> operator()(const AnswerSet& aset) const {
00491
00492 return list <AspFluentRef>(aset.getFluents().begin(), aset.getFluents().end());
00493
00494 }
00495 };
00496
00497 struct ListToAnswerSet {
00498 AnswerSet operator()(const list<AspFluent>& plan) {
00499 return AnswerSet(plan.begin(), plan.end());
00500 }
00501
00502 };
00503
00504
00505 std::vector< AnswerSet > Clingo::computeAllPlans(const std::vector<actasp::AspRule>& goal,
00506 double suboptimality) const throw () {
00507
00508 if (suboptimality < 1) {
00509 stringstream num;
00510 num << suboptimality;
00511 throw logic_error("Clingo: suboptimality value cannot be less then one, found: " + num.str());
00512 }
00513
00514 string query = generatePlanQuery(goal,true);
00515 list<AnswerSet> firstAnswerSets = krQuery(query,0,max_n,"planQuery.asp",0);
00516
00517 if (firstAnswerSets.empty())
00518 return vector<AnswerSet>();
00519
00520
00521
00522
00523 unsigned int shortestLength = firstAnswerSets.begin()->maxTimeStep()+1;
00524
00525 int maxLength = floor(suboptimality * shortestLength);
00526
00527 if (maxLength == shortestLength)
00528 return vector<AnswerSet>(firstAnswerSets.begin(), firstAnswerSets.end());
00529
00530 set< list <AspFluentRef>, LexComparator > goodPlans;
00531 transform(firstAnswerSets.begin(), firstAnswerSets.end(),inserter(goodPlans,goodPlans.begin()),AnswerSetToList());
00532
00533 query = generatePlanQuery(goal,true);
00534 list<AnswerSet> moreAnswerSets = krQuery(query,maxLength,maxLength,"planQuery.asp",0);
00535
00536
00537 list<AnswerSet>::iterator currentFirst = find_if(moreAnswerSets.begin(),moreAnswerSets.end(),PlanLongerThan(moreAnswerSets.begin()->maxTimeStep()));
00538
00539 set< list<AspFluentRef>, LexComparator > badPlans;
00540
00541
00542 IsNotLocallyOptimal isNotLocallyOptimal(&goodPlans, &badPlans,allActions,shortestLength,true);
00543
00544 list<AnswerSetRef> goodPointers(firstAnswerSets.begin(),firstAnswerSets.end());
00545 while (currentFirst != moreAnswerSets.end()) {
00546
00547
00548
00549 list<AnswerSet>::iterator currentLast = find_if(currentFirst,moreAnswerSets.end(),PlanLongerThan(currentFirst->maxTimeStep()));
00550
00551 size_t size_pre_copy = goodPointers.size();
00552
00553 remove_copy_if(currentFirst,currentLast,back_inserter(goodPointers),isNotLocallyOptimal);
00554
00555 list<AnswerSetRef>::iterator from = goodPointers.begin();
00556 advance(from,size_pre_copy);
00557 transform(from, goodPointers.end(),inserter(goodPlans,goodPlans.begin()),AnswerSetToList());
00558
00559 currentFirst = currentLast;
00560 }
00561
00562 vector<AnswerSet> finalVector(goodPointers.begin(),goodPointers.end());
00563
00564 cout << " --- good plans ---" << endl;
00565 vector< AnswerSet>::const_iterator printIt = finalVector.begin();
00566 for (; printIt != finalVector.end(); ++printIt) {
00567 copy(printIt->getFluents().begin(),printIt->getFluents().end(),ostream_iterator<string>(cout, " "));
00568 cout << endl;
00569 }
00570 cout << " ---- " << endl;
00571
00572 return finalVector;
00573
00574
00575 }
00576
00577
00578 bool Clingo::isPlanValid(const AnswerSet& plan, const std::vector<actasp::AspRule>& goal) const throw() {
00579
00580
00581 string planQuery = generatePlanQuery(goal);
00582
00583 stringstream monitorQuery(planQuery, ios_base::app | ios_base::out);
00584
00585 const AnswerSet::FluentSet &allActions = plan.getFluents();
00586 AnswerSet::FluentSet::const_iterator actionIt = allActions.begin();
00587
00588 for (int i=0; actionIt != allActions.end(); ++actionIt, ++i)
00589 monitorQuery << actionIt->toString(i) << "." << endl;
00590
00591 return !(krQuery(monitorQuery.str(),plan.getFluents().size(),plan.getFluents().size(),"monitorQuery.asp").empty());
00592 }
00593
00594 AnswerSet Clingo::currentStateQuery(const std::vector<actasp::AspRule>& query) const throw() {
00595
00596 list<AnswerSet> sets = krQuery(aspString(query,0),0,0,"stateQuery.asp");
00597
00598 return (sets.empty())? AnswerSet() : *(sets.begin());
00599 }
00600
00601 static AspRule fluent2Rule(const AspFluent& fluent) {
00602 AspRule rule;
00603 rule.head.push_back(fluent);
00604 return rule;
00605 }
00606
00607 bool Clingo::updateFluents(const std::vector<actasp::AspFluent> &observations) throw() {
00608
00609
00610 vector<AspRule> obsRules;
00611 obsRules.reserve(observations.size());
00612 transform(observations.begin(),observations.end(),back_inserter(obsRules),fluent2Rule);
00613
00614
00615 stringstream queryStream(aspString(obsRules,1), ios_base::app | ios_base::out);
00616
00617 queryStream << "noop(0)." << endl;
00618 queryStream << "#hide noop/1." << endl;
00619
00620 list<AnswerSet> currentState = krQuery(queryStream.str(),1,1,"observationQuery.asp");
00621
00622 if (currentState.empty())
00623 return false;
00624
00625 vector<AspRule> newStateRules;
00626
00627 set<AspFluent> newStateFluents = currentState.rbegin()->getFluentsAtTime(1);
00628 newStateRules.reserve(newStateFluents.size());
00629 transform(newStateFluents.begin(),newStateFluents.end(),back_inserter(newStateRules),fluent2Rule);
00630
00631
00632 ofstream currentFile((CURRENT_FILE_HOME + CURRENT_STATE_FILE).c_str());
00633
00634 currentFile << aspString(newStateRules,0);
00635 currentFile.close();
00636
00637 return true;
00638 }
00639
00640
00641
00642 std::list< std::list<AspAtom> > Clingo::query(const std::string &queryString, unsigned int initialTimeStep,
00643 unsigned int finalTimeStep) const throw() {
00644
00645
00646
00647 initialTimeStep++;
00648 finalTimeStep++;
00649
00650 string queryPath = queryDir + "generic_query.asp";
00651
00652 ofstream queryFile(queryPath.c_str());
00653 queryFile << queryString << endl;
00654 queryFile.close();
00655
00656 stringstream commandLine;
00657
00658 const string outputFilePath = queryDir + "query_output.txt";
00659
00660 if (max_time > 0) {
00661 commandLine << "timeout " << max_time << " ";
00662 }
00663
00664 stringstream iterations;
00665 iterations << "--imin=" << initialTimeStep << " --imax=" << finalTimeStep;
00666
00667
00668 commandLine << "iclingo " << iterations.str() << " " << domainDir << "*.asp " << " " << (CURRENT_FILE_HOME + CURRENT_STATE_FILE) << " " << queryPath << " > " << outputFilePath << " 0";
00669
00670
00671 if (!system(commandLine.str().c_str())) {
00672
00673 }
00674
00675 ifstream file(outputFilePath.c_str());
00676
00677 list<list <AspAtom> > allSets;
00678 bool answerFound = false;
00679
00680 string line;
00681 while(file) {
00682
00683 getline(file,line);
00684
00685 if(answerFound && line == "UNSATISFIABLE")
00686 return list<list <AspAtom> >();
00687
00688 if(line.find("Answer") != string::npos) {
00689 getline(file,line);
00690 try {
00691 stringstream predicateLine(line);
00692
00693 list<AspAtom> atoms;
00694
00695
00696 copy(istream_iterator<string>(predicateLine),
00697 istream_iterator<string>(),
00698 back_inserter(atoms));
00699
00700 allSets.push_back(atoms);
00701 } catch (std::invalid_argument& arg) {
00702
00703 }
00704 }
00705 }
00706
00707 return allSets;
00708
00709 }
00710
00711 void Clingo::reset() throw() {
00712 ofstream current((CURRENT_FILE_HOME + CURRENT_STATE_FILE).c_str());
00713 current << "";
00714 current.close();
00715 }
00716
00717 }