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