00001 #include <actasp/reasoners/Clingo4_2.h>
00002
00003 #include <actasp/AspRule.h>
00004 #include <actasp/AnswerSet.h>
00005 #include <actasp/AspAtom.h>
00006 #include <actasp/action_utils.h>
00007
00008 #include <algorithm>
00009 #include <iterator>
00010 #include <sstream>
00011 #include <fstream>
00012 #include <cstdlib>
00013 #include <iostream>
00014 #include <limits>
00015
00016 #define CURRENT_STATE_FILE std::string("/tmp/current.asp")
00017
00018 using namespace std;
00019
00020 namespace actasp {
00021
00022 Clingo4_2::Clingo4_2(const std::string& incrementalVar,
00023 const std::string& queryDir,
00024 const std::string& domainDir,
00025 const ActionSet& allActions,
00026 unsigned int max_time
00027 ) throw() :
00028 incrementalVar(incrementalVar),
00029 max_time(max_time),
00030 queryDir(queryDir),
00031 domainDir(domainDir),
00032 allActions(allActions),
00033 currentFilePath(CURRENT_STATE_FILE) {
00034
00035 if (max_time > 0 && !system("timeout 2>/dev/null"))
00036 max_time = 0;
00037
00038
00039
00040 if (this->queryDir.find_last_of("/") != (this->queryDir.length() -1))
00041 this->queryDir += "/";
00042
00043 if ((this->domainDir.find_last_of("/")) != (this->domainDir.length() -1))
00044 this->domainDir += "/";
00045
00046
00047
00048
00049 ifstream currentFile(currentFilePath.c_str());
00050 if (!currentFile.good())
00051 setCurrentState(set<AspFluent>());
00052
00053 currentFile.close();
00054 }
00055
00056 Clingo4_2::Clingo4_2(const std::string& incrementalVar,
00057 const std::string& queryDir,
00058 const std::string& domainDir,
00059 const ActionSet& allActions,
00060 const std::string& currentFilePath,
00061 unsigned int max_time
00062 ) throw() :
00063 incrementalVar(incrementalVar),
00064 max_time(max_time),
00065 queryDir(queryDir),
00066 domainDir(domainDir),
00067 allActions(allActions),
00068 currentFilePath(currentFilePath) {
00069
00070 if (max_time > 0 && !system("timeout 2>/dev/null"))
00071 max_time = 0;
00072
00073
00074
00075 if (this->queryDir.find_last_of("/") != (this->queryDir.length() -1))
00076 this->queryDir += "/";
00077
00078 if ((this->domainDir.find_last_of("/")) != (this->domainDir.length() -1))
00079 this->domainDir += "/";
00080
00081
00082
00083
00084 ifstream currentFile((currentFilePath).c_str());
00085 if (!currentFile.good())
00086 setCurrentState(set<AspFluent>());
00087
00088 currentFile.close();
00089 }
00090
00091 struct RuleToString4_2 {
00092 RuleToString4_2(unsigned int timeStepNum) {
00093 stringstream ss;
00094 ss << timeStepNum;
00095 timeStep = ss.str();
00096 }
00097
00098 RuleToString4_2(const std::string& timeStepVar) : timeStep(timeStepVar) {}
00099
00100 RuleToString4_2() : timeStep("") {}
00101
00102 std::string operator()(const AspRule& rule) const {
00103
00104 stringstream ruleStream;
00105
00106
00107 for (int i =0, size = rule.head.size(); i <size; ++i) {
00108 if (timeStep.size() >0)
00109 ruleStream << rule.head[i].toString(timeStep);
00110 else
00111 ruleStream << rule.head[i].toString();
00112
00113 if (i < (size-1))
00114 ruleStream << ", ";
00115 }
00116
00117 if (!(rule.body.empty()))
00118 ruleStream << ":- ";
00119
00120
00121 for (int i =0, size = rule.body.size(); i <size; ++i) {
00122 if (timeStep.size() >0)
00123 ruleStream << rule.body[i].toString(timeStep);
00124 else
00125 ruleStream << rule.body[i].toString();
00126
00127 if (i < (size-1))
00128 ruleStream << "| ";
00129 }
00130
00131 if (!(rule.head.empty() && rule.body.empty()))
00132 ruleStream << "." << std::endl;
00133
00134 return ruleStream.str();
00135 }
00136
00137 string timeStep;
00138 };
00139
00140 struct RuleToCumulativeString4_2 {
00141
00142 RuleToCumulativeString4_2(const std::string& timeStepVar) : timeStep(timeStepVar) {}
00143
00144 std::string operator()(const AspRule& rule) const {
00145
00146 stringstream ruleStream;
00147 unsigned int headTimeStep = 0;
00148
00149
00150 for (int i =0, size = rule.head.size(); i <size; ++i) {
00151 ruleStream << rule.head[i].toString(timeStep+"-1");
00152 headTimeStep = std::max(headTimeStep,rule.head[i].getTimeStep());
00153
00154 if (i < (size-1))
00155 ruleStream << " | ";
00156 }
00157
00158 if (!(rule.head.empty() && rule.body.empty()))
00159 ruleStream << ":- ";
00160
00161
00162 for (int i =0, size = rule.body.size(); i <size; ++i) {
00163 ruleStream << rule.body[i].toString();
00164
00165 if (i < (size-1))
00166 ruleStream << ", ";
00167 }
00168
00169 if (!rule.head.empty()) {
00170 if (!rule.body.empty())
00171 ruleStream << ", ";
00172
00173 ruleStream << timeStep << "=" << headTimeStep << "+1";
00174 }
00175
00176
00177 if (!(rule.head.empty() && rule.body.empty()))
00178 ruleStream << "." << endl;
00179
00180 return ruleStream.str();
00181 }
00182
00183 string timeStep;
00184 };
00185
00186 struct RuleToGoalString4_2 {
00187
00188 RuleToGoalString4_2(const std::string& timeStepVar) : timeStep(timeStepVar) {}
00189
00190 std::string operator()(const AspRule& rule) const {
00191
00192 stringstream ruleStream;
00193 unsigned int headTimeStep = 0;
00194
00195
00196 for (int i =0, size = rule.head.size(); i <size; ++i) {
00197 ruleStream << rule.head[i].toString(timeStep+"-1");
00198 headTimeStep = std::max(headTimeStep,rule.head[i].getTimeStep());
00199
00200 if (i < (size-1))
00201 ruleStream << " | ";
00202 }
00203
00204 if (!(rule.head.empty() && rule.body.empty()))
00205 ruleStream << ":- ";
00206
00207
00208 for (int i =0, size = rule.body.size(); i <size; ++i) {
00209 ruleStream << rule.body[i].toString(timeStep+"-1");
00210
00211 if (i < (size-1))
00212 ruleStream << ", ";
00213 }
00214
00215 if (!(rule.head.empty() && rule.body.empty()))
00216 ruleStream << ", query(" << timeStep << ")." << endl;
00217
00218 return ruleStream.str();
00219 }
00220
00221 string timeStep;
00222 };
00223
00224
00225 static string cumulativeString(const std::vector<actasp::AspRule>& query, const string& timeStepVar) {
00226
00227 stringstream aspStream;
00228 transform(query.begin(),query.end(),ostream_iterator<std::string>(aspStream),RuleToCumulativeString4_2(timeStepVar));
00229 return aspStream.str();
00230 }
00231
00232 static string aspString(const std::vector<actasp::AspRule>& query, const string& timeStepVar) {
00233
00234 stringstream aspStream;
00235 transform(query.begin(),query.end(),ostream_iterator<std::string>(aspStream),RuleToString4_2(timeStepVar));
00236 return aspStream.str();
00237 }
00238
00239 static string aspString(const std::vector<actasp::AspRule>& query, unsigned int timeStep) {
00240
00241 stringstream vs;
00242 vs << timeStep;
00243
00244 return aspString(query,vs.str());
00245 }
00246
00247 static std::list<AspFluent> parseAnswerSet(const std::string& answerSetContent) throw() {
00248
00249 stringstream predicateLine(answerSetContent);
00250
00251 list<AspFluent> predicates;
00252
00253
00254 copy(istream_iterator<string>(predicateLine),
00255 istream_iterator<string>(),
00256 back_inserter(predicates));
00257
00258 return predicates;
00259 }
00260
00261
00262 static std::list<actasp::AnswerSet> readAnswerSets(const std::string& filePath) throw() {
00263
00264 ifstream file(filePath.c_str());
00265
00266 list<AnswerSet> allSets;
00267 bool interrupted = false;
00268
00269 string line;
00270 while (file) {
00271
00272 getline(file,line);
00273
00274 if (line == "UNSATISFIABLE")
00275 return list<AnswerSet>();
00276
00277 if (line.find("INTERRUPTED : 1") != string::npos)
00278 interrupted = true;
00279
00280 if (line.find("Answer") != string::npos) {
00281 getline(file,line);
00282 while (line.find("Answer") != string::npos)
00283 getline(file,line);
00284 try {
00285 list<AspFluent> fluents = parseAnswerSet(line);
00286 allSets.push_back(AnswerSet(fluents.begin(), fluents.end()));
00287 } catch (std::invalid_argument& arg) {
00288
00289 }
00290 }
00291 }
00292
00293 if (interrupted)
00294 allSets.pop_back();
00295
00296 return allSets;
00297 }
00298
00299 static actasp::AnswerSet readOptimalAnswerSet(const std::string& filePath, const bool minimum) throw() {
00300
00301 ifstream file(filePath.c_str());
00302
00303 AnswerSet optimalAnswer;
00304 AnswerSet currentAnswer;
00305 unsigned int optimization = std::numeric_limits<unsigned int>::max();
00306 unsigned int currentOptimization;
00307 bool interrupted = false;
00308
00309 string line;
00310 while (file) {
00311
00312 getline(file,line);
00313
00314 if(line == "UNSATISFIABLE" || line == "UNKNOWN") {
00315 return optimalAnswer;
00316 }
00317
00318 if (line.find("INTERRUPTED : 1") != string::npos)
00319 interrupted = true;
00320
00321 if (line.find("Answer") != string::npos) {
00322 getline(file,line);
00323 while (line.find("Answer") != string::npos)
00324 getline(file,line);
00325 try {
00326 list<AspFluent> fluents = parseAnswerSet(line);
00327 currentAnswer = AnswerSet(fluents.begin(), fluents.end());
00328 } catch (std::invalid_argument& arg) {
00329
00330 }
00331 }
00332
00333 if (line.find("Optimization: ") != string::npos) {
00334 size_t space = line.find_first_of(" ");
00335 currentOptimization = atoi(line.substr(space+1).c_str());
00336
00337 if (minimum && (currentOptimization < optimization)) {
00338 optimalAnswer = currentAnswer;
00339 optimization = currentOptimization;
00340 }
00341 else if ((!minimum) && (currentOptimization > optimization)) {
00342 optimalAnswer = currentAnswer;
00343 optimization = currentOptimization;
00344 }
00345
00346 }
00347 }
00348
00349 return optimalAnswer;
00350 }
00351
00352 string Clingo4_2::generatePlanQuery(std::vector<actasp::AspRule> goalRules) const throw() {
00353 stringstream goal;
00354 goal << "#program volatile(" << incrementalVar << ")." << endl;
00355 goal << "#external query(" << incrementalVar << ")." << endl;
00356
00357
00358 transform(goalRules.begin(),goalRules.end(),ostream_iterator<std::string>(goal),RuleToGoalString4_2(incrementalVar));
00359
00360 goal << endl;
00361
00362 return goal.str();
00363 }
00364
00365
00366 static list<AnswerSet> filterPlans(const list<AnswerSet> unfiltered_plans, const ActionSet& allActions) {
00367
00368 list<AnswerSet> plans;
00369
00370 list<AnswerSet>::const_iterator ans = unfiltered_plans.begin();
00371 for (; ans != unfiltered_plans.end(); ++ans) {
00372 list<AspFluent> actionsOnly;
00373 remove_copy_if(ans->getFluents().begin(),ans->getFluents().end(),back_inserter(actionsOnly),not1(IsAnAction(allActions)));
00374
00375 plans.push_back(AnswerSet(actionsOnly.begin(), actionsOnly.end()));
00376 }
00377
00378 return plans;
00379 }
00380
00381 std::list<actasp::AnswerSet> Clingo4_2::minimalPlanQuery(const std::vector<actasp::AspRule>& goalRules,
00382 bool filterActions,
00383 unsigned int max_plan_length,
00384 unsigned int answerset_number) const throw() {
00385
00386 string planquery = generatePlanQuery(goalRules);
00387
00388 list<AnswerSet> answers = genericQuery(planquery,0,max_plan_length,"planQuery",answerset_number,true);
00389
00390 if (filterActions)
00391 return filterPlans(answers,allActions);
00392 else
00393 return answers;
00394
00395 }
00396
00397 struct MaxTimeStepLessThan4_2 {
00398
00399 MaxTimeStepLessThan4_2(unsigned int initialTimeStep) : initialTimeStep(initialTimeStep) {}
00400
00401 bool operator()(const AnswerSet& answer) {
00402 return !answer.getFluents().empty() && answer.maxTimeStep() < initialTimeStep;
00403 }
00404
00405 unsigned int initialTimeStep;
00406 };
00407
00408 std::list<actasp::AnswerSet> Clingo4_2::lengthRangePlanQuery(const std::vector<actasp::AspRule>& goalRules,
00409 bool filterActions,
00410 unsigned int min_plan_length,
00411 unsigned int max_plan_length,
00412 unsigned int answerset_number) const throw() {
00413
00414 string planquery = generatePlanQuery(goalRules);
00415
00416
00417
00418 std::list<actasp::AnswerSet> allplans = genericQuery(planquery,max_plan_length,max_plan_length,"planQuery",answerset_number,true);
00419
00420
00421
00422
00423 allplans.remove_if(MaxTimeStepLessThan4_2(min_plan_length));
00424
00425 if (filterActions)
00426 return filterPlans(allplans,allActions);
00427 else
00428 return allplans;
00429
00430 }
00431
00432 actasp::AnswerSet Clingo4_2::optimalPlanQuery(const std::vector<actasp::AspRule>& goalRules,
00433 bool filterActions,
00434 unsigned int max_plan_length,
00435 unsigned int answerset_number,
00436 bool minimum) const throw() {
00437
00438 string planquery = generatePlanQuery(goalRules);
00439
00440 string outputFilePath = makeQuery(planquery,max_plan_length,max_plan_length,"planQuery",answerset_number,true);
00441
00442 AnswerSet optimalPlan = readOptimalAnswerSet(outputFilePath,minimum);
00443
00444 if (filterActions) {
00445 list<AnswerSet> sets;
00446 sets.push_back(optimalPlan);
00447 return *(filterPlans(sets,allActions).begin());
00448 }
00449 else
00450 return optimalPlan;
00451 }
00452
00453 AnswerSet Clingo4_2::currentStateQuery(const std::vector<actasp::AspRule>& query) const throw() {
00454 list<AnswerSet> sets = genericQuery(aspString(query,0),0,0,"stateQuery",1,true);
00455
00456 return (sets.empty())? AnswerSet() : *(sets.begin());
00457 }
00458
00459 struct HasTimeStepZeroInHead4_2 : unary_function<const AspRule&,bool> {
00460
00461 bool operator()(const AspRule &rule) const {
00462 if (rule.head.empty())
00463 return false;
00464
00465 return rule.head[0].getTimeStep() == 0;
00466
00467 }
00468 };
00469
00470 std::list<actasp::AnswerSet> Clingo4_2::genericQuery(const std::vector<actasp::AspRule>& query,
00471 unsigned int timeStep,
00472 const std::string& fileName,
00473 unsigned int answerSetsNumber) const throw() {
00474
00475 std::vector<actasp::AspRule> base;
00476 remove_copy_if(query.begin(),query.end(),back_inserter(base), not1(HasTimeStepZeroInHead4_2()));
00477
00478 string base_part = aspString(base,0);
00479
00480 stringstream thequery(base_part, ios_base::app | ios_base::out);
00481
00482 std::vector<actasp::AspRule> cumulative;
00483 remove_copy_if(query.begin(),query.end(),back_inserter(cumulative), HasTimeStepZeroInHead4_2());
00484
00485 string cumulative_part = cumulativeString(cumulative,incrementalVar);
00486
00487 thequery << endl << "#program cumulative(" << incrementalVar << ")." << endl;
00488 thequery << cumulative_part << endl;
00489
00490 return genericQuery(thequery.str(),timeStep,timeStep,fileName,answerSetsNumber,true);
00491
00492 }
00493
00494 std::string Clingo4_2::generateMonitorQuery(const std::vector<actasp::AspRule>& goalRules,
00495 const AnswerSet& plan) const throw() {
00496 string planQuery = generatePlanQuery(goalRules);
00497
00498 stringstream monitorQuery(planQuery, ios_base::app | ios_base::out);
00499
00500 monitorQuery << "#program cumulative(" << incrementalVar << ")." << endl;
00501
00502
00503 const AnswerSet::FluentSet &actionSet = plan.getFluents();
00504 AnswerSet::FluentSet::const_iterator actionIt = actionSet.begin();
00505 vector<AspRule> plan_in_rules;
00506
00507 for (int i=1; actionIt != actionSet.end(); ++actionIt, ++i) {
00508 AspFluent action(*actionIt);
00509 action.setTimeStep(i);
00510 AspRule actionRule;
00511 actionRule.head.push_back(action);
00512 plan_in_rules.push_back(actionRule);
00513 }
00514
00515 monitorQuery << cumulativeString(plan_in_rules,"n");
00516
00517 return monitorQuery.str();
00518 }
00519
00520 std::list<actasp::AnswerSet> Clingo4_2::monitorQuery(const std::vector<actasp::AspRule>& goalRules,
00521 const AnswerSet& plan) const throw() {
00522
00523
00524
00525 string monitorQuery = generateMonitorQuery(goalRules,plan);
00526
00527 list<actasp::AnswerSet> result = genericQuery(monitorQuery,plan.getFluents().size(),plan.getFluents().size(),"monitorQuery",1,true);
00528
00529 result.remove_if(MaxTimeStepLessThan4_2(plan.getFluents().size()));
00530
00531
00532
00533
00534 return result;
00535 }
00536
00537 std::string Clingo4_2::makeQuery(const std::string& query,
00538 unsigned int initialTimeStep,
00539 unsigned int finalTimeStep,
00540 const std::string& fileName,
00541 unsigned int answerSetsNumber,
00542 bool useCurrentState
00543 ) const throw() {
00544
00545
00546 initialTimeStep++;
00547 finalTimeStep++;
00548
00549
00550
00551 string queryPath = queryDir + fileName + ".asp";
00552
00553 ofstream queryFile(queryPath.c_str());
00554 queryFile << query << endl;
00555 queryFile.close();
00556
00557 stringstream commandLine;
00558
00559 const string outputFilePath = queryDir + fileName + "_output.txt";
00560
00561 if (max_time > 0) {
00562 commandLine << "timeout " << max_time << " ";
00563 }
00564
00565 stringstream iterations;
00566 iterations << "-cimin=" << initialTimeStep;
00567
00568 iterations << " -cimax=" << finalTimeStep;
00569
00570 commandLine << "clingo " << iterations.str() << " " << queryPath << " " << domainDir << "*.asp ";
00571 if(useCurrentState)
00572 commandLine<< (currentFilePath);
00573
00574 commandLine << " > " << outputFilePath << " " << answerSetsNumber;
00575
00576
00577 if (!system(commandLine.str().c_str())) {
00578
00579 }
00580
00581 return outputFilePath;
00582 }
00583
00584 std::list<actasp::AnswerSet> Clingo4_2::genericQuery(const std::string& query,
00585 unsigned int initialTimeStep,
00586 unsigned int finalTimeStep,
00587 const std::string& fileName,
00588 unsigned int answerSetsNumber,
00589 bool useCurrentState ) const throw() {
00590
00591 string outputFilePath = makeQuery(query,initialTimeStep,finalTimeStep,fileName,answerSetsNumber,useCurrentState);
00592
00593 list<AnswerSet> allAnswers = readAnswerSets(outputFilePath);
00594
00595 return allAnswers;
00596 }
00597
00598 std::list< std::list<AspAtom> > Clingo4_2::genericQuery(const std::string& query,
00599 unsigned int timestep,
00600 const std::string& fileName,
00601 unsigned int answerSetsNumber) const throw() {
00602
00603 string outputFilePath = makeQuery(query,timestep,timestep,fileName,answerSetsNumber,true);
00604
00605 ifstream file(outputFilePath.c_str());
00606
00607 list<list <AspAtom> > allSets;
00608 bool answerFound = false;
00609
00610 string line;
00611 while (file) {
00612
00613 getline(file,line);
00614
00615 if (answerFound && line == "UNSATISFIABLE")
00616 return list<list <AspAtom> >();
00617
00618 if (line.find("Answer") != string::npos) {
00619 getline(file,line);
00620 try {
00621 stringstream predicateLine(line);
00622
00623 list<AspAtom> atoms;
00624
00625
00626 copy(istream_iterator<string>(predicateLine),
00627 istream_iterator<string>(),
00628 back_inserter(atoms));
00629
00630 allSets.push_back(atoms);
00631 } catch (std::invalid_argument& arg) {
00632
00633 }
00634 }
00635 }
00636
00637 return allSets;
00638
00639 }
00640
00641 void Clingo4_2::setCurrentState(const std::set<actasp::AspFluent>& newState) {
00642
00643
00644 ofstream currentFile((currentFilePath).c_str());
00645
00646 set<AspFluent>::const_iterator stateIt = newState.begin();
00647 for (; stateIt != newState.end(); ++stateIt)
00648 currentFile << stateIt->toString(0) << "." << endl;
00649
00650 currentFile.close();
00651
00652 }
00653
00654 std::list<actasp::AnswerSet> Clingo4_2::filteringQuery(const AnswerSet& currentState, const AnswerSet& plan,const std::vector<actasp::AspRule>& goals) {
00655
00656
00657
00658 stringstream fluentsString, minimizeString;
00659
00660 fluentsString << "#program base." << endl;
00661
00662 std::vector<actasp::AspFluent>::const_iterator fluent = currentState.getFluents().begin();
00663 for (; fluent != currentState.getFluents().end(); ++fluent) {
00664 fluentsString << "0{" << fluent->toString() << "}1." << endl;
00665 minimizeString << ":~ " << fluent->toString() << ". [1]" << endl;
00666 }
00667
00668 fluentsString << endl;
00669 minimizeString << endl;
00670
00671 string monitorString = generateMonitorQuery(goals,plan);
00672
00673
00674 stringstream total;
00675 total << fluentsString.str() << std::endl << monitorString << endl << minimizeString.str() << endl;
00676
00677
00678 return genericQuery(total.str(),plan.getFluents().size(),plan.getFluents().size(),"filterState",0,false);
00679
00680 }
00681
00682
00683 }