Commit eb54b4f4 authored by Alejandro Suarez Hernandez's avatar Alejandro Suarez Hernandez
Browse files

small improvements

parent d1b394e5
#include "blindsearch.h"
#include <iostream>
#include <queue>
#include <unordered_map>
namespace imagine_planner
{
void BreadthFirstSearch::set_parameters(const Parameters& params)
{
verbose_ = get_parameter(params, "verbose", false);
timeout_ = get_parameter(params, "timeout", 0);
max_nodes_ = get_parameter(params, "max_nodes", 0);
}
void BreadthFirstSearch::do_search(const Problem& problem)
{
typedef std::pair<PlanState::Ptr, Operator> Preceding;
......@@ -14,14 +20,15 @@ void BreadthFirstSearch::do_search(const Problem& problem)
std::queue<PlanState::Ptr> open;
open.push(problem.start);
tree[problem.start] = Preceding(nullptr, Operator());
int iteration = 0;
int expanded = 0;
int generated = 0;
while (not (open.empty() or timeout() or memout(open.size())))
{
PlanState::Ptr state = open.front();
open.pop();
auto all_ops = problem.domain->instantiate_all(state);
stats_["expanded"] += 1;
stats_["generated"] += all_ops.size();
expanded += 1;
generated += all_ops.size();
for (const Operator& op : all_ops)
{
auto successor = op(state);
......@@ -31,29 +38,37 @@ void BreadthFirstSearch::do_search(const Problem& problem)
if ((*problem.goal)(*successor))
{
/* Goal condition satisfied */
complete_stats();
fill_stats(expanded, generated);
result_ = FOUND;
plan_ = Plan::reconstruct_path(problem, tree, successor);
plan_ = Plan::reconstruct_path(tree, successor);
if (verbose_)
{
PLANNER_LOG("Plan with " << plan_.size() << " action(s) found", INFO);
}
return;
}
open.push(successor);
}
}
if (verbose_ > 0 and iteration%100 == 0)
if (verbose_ and expanded%100 == 1)
{
/* log feedback */
std::cout << "#expanded nodes: " << stats_["expanded"]
<<", #generated nodes: " << stats_["generated"] << std::endl;
PLANNER_LOG("#expanded nodes: " << expanded <<
", #generated nodes: " << generated, INFO);
}
++iteration;
}
/* Either unsolvable problem or timeout/memout */
complete_stats();
if (open.empty()) result_ = UNSOLVABLE;
fill_stats(expanded, generated);
if (open.empty())
{
result_ = UNSOLVABLE;
if (verbose_) PLANNER_LOG("Unsolvable problem", WARNING);
}
else
{
result_ = NOT_FOUND;
msg_ = timeout()? "timeout" : "memout";
if (verbose_) PLANNER_LOG("Could not solve problem: " << msg_, ERROR);
}
}
......@@ -67,9 +82,11 @@ bool BreadthFirstSearch::memout(std::size_t open_size) const
return max_nodes_ > 0 and open_size > max_nodes_;
}
void BreadthFirstSearch::complete_stats()
void BreadthFirstSearch::fill_stats(double expanded, double generated)
{
stats_["branching"] = stats_["generated"] / stats_["expanded"];
stats_["expanded"] = expanded;
stats_["generated"] = generated;
stats_["branching"] = generated/expanded;
}
} /* end namespace */
......
......@@ -10,8 +10,9 @@ class BreadthFirstSearch : public Planner
{
public:
BreadthFirstSearch(int verbose=0, double timeout=0, std::size_t max_nodes=0) :
verbose_(verbose), timeout_(timeout), max_nodes_(max_nodes) {}
BreadthFirstSearch() { set_parameters({}); }
virtual void set_parameters(const Planner::Parameters& params) override;
protected:
......@@ -23,14 +24,66 @@ class BreadthFirstSearch : public Planner
bool memout(std::size_t open_size) const;
void complete_stats();
void fill_stats(double expanded, double generated);
int verbose_;
bool verbose_;
double timeout_;
std::size_t max_nodes_;
int max_nodes_;
};
// [TO DO] Iterative Deepening Search
// [TO DO] Re-implement Iterative Deepening Search
// Previous implementation of IDS
//class IDSPlanner : public Planner
//{
//private:
//typedef std::unordered_set<PlanState, std::hash<Hashable>> ClosedSet;
//const Problem* problem_;
//ClosedSet seen_;
//int max_depth_;
//double elapsed_;
//bool ids(const PlanState& state, Plan& plan, std::size_t depth)
//{
//if (problem_->goal(state)) return true;
//if (depth == 0) return false;
//seen_.insert(state);
//std::vector<Operator> instantiations = problem_->domain.instantiate_all(state);
//for (const Operator& op : instantiations)
//{
//PlanState next = op(state);
//if (seen_.count(next)) continue;
//plan.push_back({op, next});
//if (ids(next, plan, depth-1)) return true;
//plan.pop_back();
//}
//seen_.erase(state);
//return false;
//}
//public:
//IDSPlanner(int max_depth=100) : max_depth_(100) {}
//virtual Plan operator()(const Problem& problem) override
//{
//Plan plan;
//problem_ = &problem;
//StopWatch sw;
//for (int depth = 0; depth < max_depth_; ++depth)
//{
//if (ids(problem_->start, plan, depth)) break;
//}
//elapsed_ = sw.time();
//seen_.clear();
//return plan;
//}
//double get_elapsed() const { return elapsed_; }
//};
}
......
......@@ -9,6 +9,8 @@ namespace imagine_planner
// Implementation of Operator's methods
Operator Operator::NOP;
Operator::Operator(const std::string& name, const std::string& pre,
const std::string& eff) : name_(name)
{
......@@ -42,12 +44,17 @@ std::string Operator::to_str() const
PlanState::Ptr Operator::operator()(const PlanState::Ptr& state) const
{
PlanState::Ptr new_state(nullptr);
if (not is_ground())
{
throw ImaginePlannerException("Trying to apply unground operator");
}
auto new_state = std::make_shared<PlanState>(*state);
(*eff_)(*new_state, *sigma_);
if (is_nop()) new_state = state;
else if (check_precondition(state))
{
new_state = std::make_shared<PlanState>(*state);
(*eff_)(*new_state, *sigma_);
}
return new_state;
}
......
......@@ -16,6 +16,8 @@ class Operator : public Stringifiable
typedef std::shared_ptr<const Operator> Ptr;
static Operator NOP;
Operator() {}
Operator(const std::string& name, const Query::Ptr& pre,
......@@ -37,6 +39,8 @@ class Operator : public Stringifiable
bool is_ground() const { return (bool)sigma_; }
bool is_nop() const { return name_.empty(); }
bool check_precondition(const PlanState::Ptr& state) const;
PlanState::Ptr operator()(const PlanState::Ptr& state) const;
......
......@@ -10,64 +10,14 @@ using namespace imagine_planner;
#if 1
#define INFO(x)\
std::cout << "\e[1m\e[32m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl;
std::cout << "\e[32m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl;
#else
#define INFO(x)
#endif
#define ERROR(x)\
std::cout << "\e[1m\e[31m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl;
std::cout << "\e[31m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl;
//class IDSPlanner : public Planner
//{
//private:
//typedef std::unordered_set<PlanState, std::hash<Hashable>> ClosedSet;
//const Problem* problem_;
//ClosedSet seen_;
//int max_depth_;
//double elapsed_;
//bool ids(const PlanState& state, Plan& plan, std::size_t depth)
//{
//if (problem_->goal(state)) return true;
//if (depth == 0) return false;
//seen_.insert(state);
//std::vector<Operator> instantiations = problem_->domain.instantiate_all(state);
//for (const Operator& op : instantiations)
//{
//PlanState next = op(state);
//if (seen_.count(next)) continue;
//plan.push_back({op, next});
//if (ids(next, plan, depth-1)) return true;
//plan.pop_back();
//}
//seen_.erase(state);
//return false;
//}
//public:
//IDSPlanner(int max_depth=100) : max_depth_(100) {}
//virtual Plan operator()(const Problem& problem) override
//{
//Plan plan;
//problem_ = &problem;
//StopWatch sw;
//for (int depth = 0; depth < max_depth_; ++depth)
//{
//if (ids(problem_->start, plan, depth)) break;
//}
//elapsed_ = sw.time();
//seen_.clear();
//return plan;
//}
//double get_elapsed() const { return elapsed_; }
//};
void head_and_tail(const std::string& rule,
std::string& head,
......@@ -193,16 +143,19 @@ int main(int argc, char *argv[])
return -1;
}
problem.domain = std::make_shared<Domain>(domain_name, dbpath, 0);
BreadthFirstSearch planner(1);
BreadthFirstSearch planner;
planner.set_parameters({{"verbose", "true"}});
planner.set_logger(Logger::Ptr(new StdLogger));
planner.search(problem);
Planner::Statistics stats = planner.get_stats();
INFO("Initial state: " << *problem.start);
INFO("Goal condition: " << *problem.goal);
INFO("Plan found: " << planner.get_plan());
INFO("Elapsed (ms): " << stats["elapsed"]*1000);
INFO("#Expaned nodes: " << stats["expanded"]);
INFO("#Expanded nodes: " << stats["expanded"]);
INFO("#Generated nodes: " << stats["generated"]);
INFO("Avg. branching factor: " << stats["branching"]);
INFO("Message: " << planner.get_message());
//IDSPlanner planner(50);
//Plan plan = planner(problem);
//INFO("Initial state: " << problem.start);
......
#include "imagine-planner.h"
#include <iostream>
#include <sstream>
namespace imagine_planner
......@@ -18,25 +19,23 @@ std::string Plan::to_str() const
return oss.str();
}
bool Plan::validate(const PlanState::Ptr& state) const
{
PlanState::Ptr last = state;
for (std::size_t step = cursor_; step < ops_.size(); ++step)
{
const Operator& op = ops_[step];
if (op.check_precondition(last)) last = op(last);
else return false;
}
return (*problem_.goal)(*last);
}
// StdLogger methods
const Operator& Plan::next_action()
void StdLogger::log(const std::string& msg, Logger::Level level) const
{
if (cursor_ == ops_.size())
switch (level)
{
throw ImaginePlannerException("Cursor is already at the end");
case Logger::INFO:
std::cout << "\e[34m";
break;
case Logger::WARNING:
std::cout << "\e[33m";
break;
case Logger::ERROR:
std::cout << "\e[31m";
break;
}
return ops_[cursor_++];
std::cout << msg << "\e[0m" << std::endl;
}
......@@ -45,15 +44,80 @@ const Operator& Plan::next_action()
Planner::Result Planner::search(const Problem& problem)
{
start_ = std::clock();
do_search(problem);
if ((*problem.goal)(*problem.start))
{
/* take care of the case in which the goal is already satisfied */
result_ = FOUND;
plan_ = Plan();
}
else do_search(problem);
stats_["elapsed"] = elapsed();
if (result_ == FOUND) msg_ = "found";
else if (result_ == UNSOLVABLE) msg_ = "unsolvable";
return result_;
}
void Planner::reset()
{
plan_ = Plan();
stats_.clear();
result_ = NOT_STARTED;
msg_.clear();
}
bool Planner::get_parameter(
const Parameters& params, const std::string& key, bool def)
{
auto it = params.find(key);
bool ret = def;
if (it != params.end())
{
std::string value = it->second;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
ret = value == "true" or value == "1";
}
return ret;
}
int Planner::get_parameter(
const Parameters& params, const std::string& key, int def)
{
auto it = params.find(key);
int ret = def;
if (it != params.end()) ret = std::stoi(it->second);
return ret;
}
double Planner::get_parameter(
const Parameters& params, const std::string& key, double def)
{
auto it = params.find(key);
int ret = def;
if (it != params.end()) ret = std::stod(it->second);
return ret;
}
std::string Planner::get_parameter(
const Parameters& params, const std::string& key, const std::string& def)
{
auto it = params.find(key);
return it == params.end()? def : it->second;
}
double Planner::elapsed() const
{
return (std::clock() - start_)/(double)CLOCKS_PER_SEC;
}
void Planner::log(const std::string& msg, Logger::Level level) const
{
if (logger_)
{
std::string timestamp = std::string("[") +
std::to_string(elapsed()*1000) + "ms] ";
logger_->log(timestamp + msg, level);
}
}
}
......@@ -5,7 +5,12 @@
#include <algorithm>
#include <ctime>
#include <utility>
#include <sstream>
#define PLANNER_LOG(stream, level) {\
std::ostringstream oss; oss << stream;\
this->log(oss.str(), imagine_planner::Logger::level);\
}
namespace imagine_planner
......@@ -28,13 +33,15 @@ class Plan : public Stringifiable
{
public:
typedef std::vector<Operator> OperatorV;
typedef std::vector<PlanState::Ptr> StateV;
typedef std::shared_ptr<const Plan> Ptr;
template<class Tree>
static Plan reconstruct_path(const Problem& problem, const Tree& tree, PlanState::Ptr endstate)
static Plan reconstruct_path(const Tree& tree, PlanState::Ptr endstate)
{
std::vector<Operator> ops;
std::vector<PlanState::Ptr> states{endstate};
OperatorV ops;
StateV states{endstate};
auto preceding = tree.find(endstate)->second;
while (preceding.first)
{
......@@ -44,57 +51,101 @@ class Plan : public Stringifiable
}
std::reverse(ops.begin(), ops.end());
std::reverse(states.begin(), states.end());
return Plan(problem, ops, states);
return Plan(ops, states);
}
Plan(const Problem& problem={}, const std::vector<Operator>& ops={},
const std::vector<PlanState::Ptr>& states={}) :
problem_(problem), ops_(ops), states_(states), cursor_(0) {}
Plan() {}
virtual std::string to_str() const override;
Plan(const OperatorV& ops, const StateV& states) :
ops_(ops), states_(states) {}
bool validate(const PlanState::Ptr& state) const;
virtual std::string to_str() const override;
bool completed() const { return cursor_ == ops_.size(); }
const Operator& operator[](std::size_t idx) const { return ops_[idx]; }
const Operator& next_action();
OperatorV::const_iterator begin() const { return ops_.begin(); }
void reset() { cursor_ = 0; }
OperatorV::const_iterator end() const { return ops_.end(); }
const std::vector<Operator>& operations() const { return ops_; }
std::size_t size() const { return ops_.size(); }
const std::vector<PlanState::Ptr>& states() const { return states_; }
const StateV& get_states() const { return states_; }
private:
Problem problem_;
std::vector<Operator> ops_;
std::vector<PlanState::Ptr> states_;
std::size_t cursor_;
OperatorV ops_;
StateV states_;
};
class Logger
{
public:
typedef std::unique_ptr<const Logger> Ptr;
enum Level {INFO, WARNING, ERROR};
virtual void log(const std::string& msg, Level level) const =0;
virtual ~Logger() {}
};
class StdLogger : public Logger
{
public:
void log(const std::string& msg, Logger::Level level=INFO) const override;
};
class Planner
{
public:
enum Result{FOUND, UNSOLVABLE, NOT_FOUND};
enum Result{FOUND, UNSOLVABLE, NOT_FOUND, NOT_STARTED};
typedef std::map<std::string, double> Statistics;
typedef std::map<std::string, std::string> Parameters;
Planner() : result_(NOT_STARTED) {}
void set_logger(Logger::Ptr&& logger) { logger_ = std::move(logger); }
virtual void set_parameters(const Parameters& params) =0;
Result search(const Problem& problem);
const Statistics& get_stats() const { return stats_; }
Result get_result() const { return result_; }
const std::string& get_message() const { return msg_; }
const Plan& get_plan() const { return plan_; }
virtual ~Planner() {};
virtual void reset();
protected:
static bool get_parameter(
const Parameters& params, const std::string& key, bool def);
static int get_parameter(
const Parameters& params, const std::string& key, int def);
static double get_parameter(
const Parameters& params, const std::string& key, double def);
std::string get_parameter(
const Parameters& params, const std::string& key, const std::string& def);
virtual void do_search(const Problem& problem) =0;
double elapsed() const;
void log(const std::string& msg, Logger::Level level=Logger::INFO) const;
Plan plan_;
std::map<std::string, double> stats_;
Result result_;
......@@ -103,6 +154,8 @@ class Planner
private:
std::clock_t start_;
Logger::Ptr logger_;
};
} /* end of imagine_planner namespace */
......
......@@ -348,7 +348,7 @@ class PlanState : public Stringifiable,
return std::make_shared<PlanState>(predicates);
}
PlanState(const Container& predicates)
PlanState(const Container& predicates={})
: predicates_(predicates), hash_cached_(false) {}
template <typename InputIterator>
......@@ -390,6 +390,8 @@ class PlanState : public Stringifiable,
CIter end() const { return predicates_.end(); }