Fawkes API  Fawkes Development Version
stn.cpp
1 
2 /***************************************************************************
3  * stn.cpp - stn-generator
4  *
5  * Created: Sat May 6 20:16:21 2017
6  * Copyright 2017 Matthias Loebach
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "stn.h"
23 
24 #include <pddl_parser/pddl_parser.h>
25 
26 #include <exception>
27 #include <fstream>
28 #include <iostream>
29 
30 namespace fawkes {
31 namespace stn {
32 
33 /** @class Stn "stn.h"
34  * A Simple Temporal Network.
35  */
36 
37 /** Constructor.
38  * @param logger The logger to log to.
39  */
40 Stn::Stn(fawkes::Logger *logger) : logger_(logger)
41 {
42 }
43 
44 /** Constructor.
45  * @param logger The logger to log to.
46  * @param classic_dom_path The path to the domain file to write to.
47  */
48 Stn::Stn(fawkes::Logger *logger, const std::string &classic_dom_path)
49 : logger_(logger), classic_dom_path_(classic_dom_path)
50 {
51  gen_classic_dom_ = true;
52 }
53 
54 /** Destructor */
56 {
57 }
58 
59 /** Add the given DomainAction to the STN.
60  * @param action The action to add.
61  */
62 void
63 Stn::add_domain_action(const DomainAction &action)
64 {
65  domain_actions_.push_back(action);
66 }
67 
68 /** Add a (grounded action).
69  * @param name The name of the action/operator.
70  * @param params The parameters of the action.
71  */
72 void
73 Stn::add_plan_action(const std::string &name, const std::string &params)
74 {
75  plan_actions_.push_back(plan_action{name, params});
76 }
77 
78 /** Set the initial state.
79  * The resulting initial state is the state after applying the effects of the
80  * given action.
81  * @param action The action whose effects define the initial state.
82  */
83 void
85 {
86  initial_state_ = action;
87 }
88 
89 /** Read the initial state from the given PDDL problem.
90  * @param pddl_problem_string the PDDL rpboelm as (unparsed) string.
91  */
92 void
93 Stn::read_initial_state(const std::string &pddl_problem_string)
94 {
96  pddl_parser::Problem prob = parser.parseProblem(pddl_problem_string);
97 
98  log_info("Parsing PDDL Problem for STN generation.");
99 
100  log_info("Parsed problem " + prob.name);
101  std::vector<stn::Predicate> init_predicates;
102  for (pddl_parser::Expression pred : prob.init) {
103  std::vector<std::string> attrs;
104  std::string log_string = "Adding init-predicate "
105  + boost::get<pddl_parser::Predicate>(pred).function
106  + " with arguments:";
107  for (pddl_parser::Expression a : boost::get<pddl_parser::Predicate>(pred).arguments) {
108  attrs.push_back(boost::get<pddl_parser::Atom>(a));
109  log_string += " " + boost::get<pddl_parser::Atom>(a);
110  }
111  log_info(log_string);
112  stn::Predicate init_pred(boost::get<pddl_parser::Predicate>(pred).function, true, attrs);
113  init_predicates.push_back(init_pred);
114  }
115  stn::StnAction init_action(prob.name, {}, init_predicates, std::string(""));
116  set_initial_state(init_action);
117 }
118 
119 /** Set the domain of the STN to the given PDDL domain.
120  * This parses the given domain and processes all actions in the domain.
121  * It also adds all temporal and conditional breakups defined in the domain to
122  * the STN.
123  * @param pddl_domain_string the PDDL domain as (unparsed) string.
124  */
125 void
126 Stn::set_pddl_domain(const std::string &pddl_domain_string)
127 {
129  pddl_parser::Domain dom = parser.parseDomain(pddl_domain_string);
130 
131  log_info("Loading extended PDDL domain into STN.");
132 
133  for (auto &action : dom.actions) {
134  log_info("Processing action " + action.name);
135  std::vector<std::string> params;
136  for (auto &param : action.action_params) {
137  params.push_back(param.first);
138  }
139  std::vector<Predicate> preconds;
140  build_pred_list(action.precondition, &preconds, true);
141  std::vector<Predicate> effects;
142  build_pred_list(action.effect, &effects, true);
143  int duration = action.duration;
144  std::vector<std::string> cond_breakups;
145  log_info(std::to_string(action.cond_breakup.which()));
146  if (action.cond_breakup.which() == 1) { // only if type is Expression
147  build_breakup_list(action.cond_breakup, &cond_breakups);
148  }
149  std::vector<std::string> temp_breakups;
150  if (action.temp_breakup.which() == 1) { // only if type is Expression
151  build_breakup_list(action.temp_breakup, &temp_breakups);
152  }
153  DomainAction da(action.name, params, preconds, effects, duration, cond_breakups, temp_breakups);
154  domain_actions_.push_back(da);
155  }
156 
157  log_info("Initialized " + std::to_string(domain_actions_.size()) + " domain actions");
158 
159  if (gen_classic_dom_) {
160  log_info("Generation of classic domain file is configured, starting...");
161  generate_classic_pddl_domain(&dom, classic_dom_path_);
162  }
163 }
164 
165 /* For now this only works with the not and and operators
166  * to combine multiple predicates
167  */
168 void
169 Stn::build_pred_list(pddl_parser::Expression e, std::vector<Predicate> *preconds, bool condition)
170 {
171  pddl_parser::Atom function = boost::get<pddl_parser::Predicate>(e).function;
172  if (function == "and" || function == "not") {
173  if (function == "not") {
174  condition = !condition;
175  }
176  for (auto &child : boost::get<pddl_parser::Predicate>(e).arguments) {
177  build_pred_list(child, preconds, condition);
178  }
179  } else {
180  std::vector<std::string> args;
181  for (auto &arg : boost::get<pddl_parser::Predicate>(e).arguments) {
182  args.push_back(boost::get<std::string>(arg));
183  }
184  Predicate p(boost::get<pddl_parser::Predicate>(e).function, condition, args);
185  preconds->push_back(p);
186  log_info("Added " + boost::get<pddl_parser::Predicate>(e).function);
187  }
188 }
189 
190 void
191 Stn::build_breakup_list(pddl_parser::Expression e, std::vector<std::string> *breakups)
192 {
193  pddl_parser::Atom function = boost::get<pddl_parser::Predicate>(e).function;
194  // ignore negations, we only take the name into account
195  if (function == "and" || function == "not") {
196  for (auto &child : boost::get<pddl_parser::Predicate>(e).arguments) {
197  build_breakup_list(child, breakups);
198  }
199  } else {
200  std::string pred_name = boost::get<pddl_parser::Predicate>(e).function;
201  std::cout << "Adding breakup " << pred_name << std::endl;
202  breakups->push_back(pred_name);
203  }
204 }
205 
206 /** Regenerate the STN. */
207 void
209 {
210  stn_actions_.clear();
211  //stn_actions_.push_back(initial_state_);
212 
213  for (plan_action pa : plan_actions_) {
214  std::vector<DomainAction>::iterator it = domain_actions_.begin();
215  for (; it != domain_actions_.end(); ++it) {
216  if (it->getName() == pa.name) {
217  break;
218  }
219  }
220  if (it == domain_actions_.end())
221  throw("could not find fitting DomainAction");
222  DomainAction da = *(it);
223 
224  stn_actions_.push_back(da.generateStnAction(pa.name, pa.params));
225  }
226  std::cout << "Imported " << stn_actions_.size() << " actions into STN" << std::endl;
227 
228  for (int i = stn_actions_.size() - 1; i >= 0; i--) {
229  std::vector<StnAction> candidate_actions =
230  std::vector<StnAction>(stn_actions_.begin(), stn_actions_.begin() + i);
231  try {
232  stn_actions_.at(i).genConditionalActions(candidate_actions);
233  } catch (std::exception &e) {
234  std::cout << "ERROR stn.cpp:" << e.what() << std::endl;
235  }
236  }
237 
238  std::vector<Predicate> predicates;
239  for (std::vector<StnAction>::iterator it = stn_actions_.begin(); it != stn_actions_.end(); ++it) {
240  // add conditional edges
241  for (auto const &cond_action : it->condActionIds()) {
242  std::pair<StnAction, StnAction> edge(findActionById(cond_action), findActionById(it->id()));
243  cond_edges_.push_back(edge);
244  }
245  // add temporal edges
246  bool break_edge = false;
247  for (Predicate p : predicates) {
248  if (it->checkForBreakup(EdgeType::TEMPORAL, p)) {
249  break_edge = true;
250  break;
251  }
252  }
253  if (!break_edge && it != stn_actions_.begin()) {
254  std::pair<StnAction, StnAction> edge(findActionById((it - 1)->id()),
255  findActionById(it->id()));
256  temp_edges_.push_back(edge);
257  }
258  // handle predicates
259  for (Predicate p : it->effects()) {
260  if (p.condition()) {
261  std::vector<Predicate>::iterator it = std::find(predicates.begin(), predicates.end(), p);
262  if (it == predicates.end()) {
263  predicates.push_back(p);
264  //std::cout << "Added " << p;
265  }
266  } else {
267  //std::cout << "Check for erase: " << p;
268  Predicate neg_pred(p.name(), true, p.attrs());
269  std::vector<Predicate>::iterator it =
270  std::find(predicates.begin(), predicates.end(), neg_pred);
271  if (it != predicates.end()) {
272  //std::cout << "Erased " << (*it);
273  predicates.erase(it);
274  }
275  }
276  }
277  }
278  //generate missing temporal links
279  for (auto &a : stn_actions_) {
280  bool no_temp_edge = true;
281  for (auto &e : temp_edges_) {
282  if (e.first == a) {
283  no_temp_edge = false;
284  break;
285  }
286  }
287  if (no_temp_edge) {
288  for (auto &ce : cond_edges_) {
289  if (ce.first == a) {
290  std::pair<StnAction, StnAction> edge(a, ce.second);
291  temp_edges_.push_back(edge);
292  }
293  }
294  }
295  }
296 }
297 
298 /** Render a graph representation of the STN.
299  * This writes the graph representation to the file stn.png.
300  */
301 void
303 {
304  Agraph_t *G;
305  GVC_t * gvc;
306 
307  gvc = gvContext();
308  char graph_name[] = "STN";
309 
310  G = agopen(graph_name, Agdirected, 0);
311 
312  std::map<size_t, Agnode_t *> node_map;
313 
314  for (StnAction a : stn_actions_) {
315  std::string node_name = a.genGraphNodeName();
316  node_map.insert(std::make_pair(a.id(), agnode(G, (char *)node_name.c_str(), true)));
317  }
318 
319  std::vector<Agedge_t *> edge_list;
320  for (auto &edge : cond_edges_) {
321  Agnode_t *node1 = node_map.at(edge.first.id());
322  Agnode_t *node2 = node_map.at(edge.second.id());
323  Agedge_t *graph_edge = agedge(G, node1, node2, (char *)"conditional", true);
324  edge_list.push_back(graph_edge);
325 
326  std::string edge_label = edge.second.genConditionEdgeLabel(edge.first.id());
327  agsafeset(graph_edge,
328  (char *)"label",
329  agstrdup_html(G, (char *)edge_label.c_str()),
330  (char *)"");
331  agsafeset(graph_edge, (char *)"color", (char *)"red", (char *)"");
332  }
333 
334  for (auto &edge : temp_edges_) {
335  Agnode_t *node1 = node_map.at(edge.first.id());
336  Agnode_t *node2 = node_map.at(edge.second.id());
337  Agedge_t *graph_edge = agedge(G, node1, node2, (char *)"temporal", true);
338  edge_list.push_back(graph_edge);
339 
340  std::string edge_label = edge.second.genTemporalEdgeLabel();
341  agsafeset(graph_edge,
342  (char *)"label",
343  agstrdup_html(G, (char *)edge_label.c_str()),
344  (char *)"");
345  agsafeset(graph_edge, (char *)"color", (char *)"blue", (char *)"");
346  }
347 
348  gvLayout(gvc, G, "dot");
349  gvRenderFilename(gvc, G, "png", "stn.png");
350 
351  gvFreeLayout(gvc, G);
352  agclose(G);
353  gvFreeContext(gvc);
354 }
355 
356 /** Get a BSON representation of the STN.
357  * @return A vector of BSON objects, each element is an action.
358  */
359 std::vector<mongo::BSONObj>
361 {
362  std::vector<mongo::BSONObj> stn;
363  for (auto &action : stn_actions_) {
364  mongo::BSONObjBuilder bson_action;
365  bson_action << "id" << static_cast<long long>(action.id());
366  bson_action << "name" << action.name();
367  bson_action << "duration" << static_cast<long long>(action.duration());
368  mongo::BSONArrayBuilder cond_actions;
369  for (auto &cond : action.condActionIds()) {
370  cond_actions << static_cast<long long>(cond);
371  }
372  bson_action << "cond-actions" << cond_actions.arr();
373  mongo::BSONArrayBuilder opts_arr;
374  std::stringstream opts_ss(action.opts());
375  std::istream_iterator<std::string> end;
376  for (std::istream_iterator<std::string> it(opts_ss); it != end; it++) {
377  opts_arr << *it;
378  }
379  bson_action << "opts" << opts_arr.arr();
380  stn.push_back(bson_action.obj());
381  }
382  return stn;
383 }
384 
385 StnAction
386 Stn::findActionById(size_t id)
387 {
388  for (StnAction a : stn_actions_) {
389  if (a.id() == id) {
390  return a;
391  }
392  }
393  throw(" Action with id " + std::to_string(id) + " not found");
394 }
395 
396 void
397 Stn::log_warn(const std::string &s)
398 {
399  log(s, LogLevel::WARN);
400 }
401 
402 void
403 Stn::log_info(const std::string &s)
404 {
405  log(s, LogLevel::INFO);
406 }
407 
408 void
409 Stn::log_debug(const std::string &s)
410 {
411  log(s, LogLevel::DEBUG);
412 }
413 void
414 Stn::log(const std::string &s, Stn::LogLevel log_level)
415 {
416  std::string name = "STN";
417  switch (log_level) {
418  case LogLevel::WARN: logger_->log_warn(name.c_str(), "%s", s.c_str()); break;
419  case LogLevel::INFO: logger_->log_info(name.c_str(), "%s", s.c_str()); break;
420  case LogLevel::DEBUG: logger_->log_debug(name.c_str(), "%s", s.c_str()); break;
421  }
422 }
423 
424 void
425 Stn::generate_classic_pddl_domain(pddl_parser::Domain *dom, const std::string &classic_dom_path)
426 {
427  log_info("Writing domain to " + classic_dom_path);
428  std::ofstream out(classic_dom_path);
429 
430  out << "(define (domain " << dom->name << ")" << std::endl;
431 
432  out << "\t(:requirements";
433  for (auto &req : dom->requirements) {
434  out << " :" << req;
435  }
436  out << ")" << std::endl;
437 
438  out << "\t(:types" << std::endl;
439  for (auto &type : dom->types) {
440  out << "\t\t" << type.first << " - " << type.second << std::endl;
441  }
442  out << "\t)" << std::endl;
443 
444  out << "\t(:constants" << std::endl;
445  for (auto &const_type : dom->constants) {
446  out << "\t\t";
447  for (auto &constant : const_type.first) {
448  out << constant << " ";
449  }
450  out << "- " << const_type.second << std::endl;
451  }
452  out << "\t)" << std::endl;
453 
454  out << "\t(:predicates" << std::endl;
455  for (auto &predicate : dom->predicates) {
456  out << "\t\t(" << predicate.first;
457  for (auto &pred_type : predicate.second) {
458  out << " ?" << pred_type.first << " - " << pred_type.second;
459  }
460  out << ")" << std::endl;
461  }
462  out << "\t)" << std::endl;
463 
464  for (auto &action : dom->actions) {
465  out << "\t(:action " << action.name << std::endl;
466  out << "\t\t:parameters (";
467  for (auto &param : action.action_params) {
468  out << " ?" << param.first << " - " << param.second;
469  }
470  out << ")" << std::endl;
471  out << "\t\t:precondition" << std::endl << "\t\t\t";
472  output_pred_list(action.precondition, out);
473 
474  out << std::endl << "\t\t:effect" << std::endl << "\t\t\t";
475  output_pred_list(action.effect, out);
476 
477  out << std::endl << "\t)" << std::endl;
478  }
479 
480  out << ")";
481 
482  out.close();
483 }
484 
485 void
486 Stn::output_pred_list(pddl_parser::Expression e, std::ofstream &out)
487 {
488  pddl_parser::Atom function = boost::get<pddl_parser::Predicate>(e).function;
489  if (function == "not" || function == "and") {
490  if (function == "not") {
491  out << "(not ";
492  } else if (function == "and") {
493  out << "(and ";
494  }
495  for (auto &child : boost::get<pddl_parser::Predicate>(e).arguments) {
496  output_pred_list(child, out);
497  }
498  out << ") ";
499  } else {
500  out << "(" << boost::get<pddl_parser::Predicate>(e).function;
501  for (auto &arg : boost::get<pddl_parser::Predicate>(e).arguments) {
502  out << " " << boost::get<std::string>(arg);
503  }
504  out << ")";
505  }
506 }
507 
508 } // namespace stn
509 } // namespace fawkes
void read_initial_state(const std::string &pddl_problem_string)
Read the initial state from the given PDDL problem.
Definition: stn.cpp:93
Parse a PDDL domain file or problem.
Definition: pddl_parser.h:33
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
static Domain parseDomain(const std::string pddl_domain)
Parse the PDDL domain.
Definition: pddl_parser.cpp:46
Fawkes library namespace.
static Problem parseProblem(const std::string pddl_problem)
Parse the PDDL problem.
Definition: pddl_parser.cpp:73
void generate()
Regenerate the STN.
Definition: stn.cpp:208
std::vector< Action > actions
A list of actions defined in the domain.
Definition: pddl_ast.h:113
virtual ~Stn()
Destructor.
Definition: stn.cpp:55
A representation of a Predicate in the STN.
Definition: predicate.h:32
Stn(fawkes::Logger *logger)
Constructor.
Definition: stn.cpp:40
void set_initial_state(const StnAction &action)
Set the initial state.
Definition: stn.cpp:84
pairs_multi_consts constants
A typed list of constants defined in the domain.
Definition: pddl_ast.h:107
void add_plan_action(const std::string &name, const std::string &params)
Add a (grounded action).
Definition: stn.cpp:73
void set_pddl_domain(const std::string &pddl_domain_string)
Set the domain of the STN to the given PDDL domain.
Definition: stn.cpp:126
std::vector< predicate_type > predicates
A list of predicate names in the domain, including the types of their arguments.
Definition: pddl_ast.h:111
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
A representation of an action used by the STN generator.
Definition: domain_action.h:39
pairs_type types
A list of types with their super types.
Definition: pddl_ast.h:105
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
std::vector< mongo::BSONObj > get_bson()
Get a BSON representation of the STN.
Definition: stn.cpp:360
std::vector< std::string > requirements
A list of PDDL features required by the domain.
Definition: pddl_ast.h:103
std::vector< Expression > init
A list of facts that are initially true.
Definition: pddl_ast.h:128
void drawGraph()
Render a graph representation of the STN.
Definition: stn.cpp:302
StnAction generateStnAction(const std::string &name, const std::string &params)
Generate an StnAction from the DomainAction.
std::string name
The name of the problem.
Definition: pddl_ast.h:122
std::string name
The name of the domain.
Definition: pddl_ast.h:101
A structured representation of a PDDL problem.
Definition: pddl_ast.h:119
An action representation within an STN.
Definition: stn_action.h:40
A structured representation of a PDDL domain.
Definition: pddl_ast.h:98
Interface for logging.
Definition: logger.h:41