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

added prolog support

parent ef970f7e
......@@ -2,13 +2,20 @@
SET(sources imagine-planner.cpp types.cpp)
# application header files
SET(headers imagine-planner.h types.h)
# Boost
FIND_PACKAGE(Boost COMPONENTS system filesystem unit_test_framework REQUIRED)
# Swi-pl
include(FindPkgConfig)
pkg_check_modules(SWIPL REQUIRED swipl)
# locate the necessary dependencies
# add the necessary include directories
INCLUDE_DIRECTORIES(. ${Boost_INCLUDE_DIR})
INCLUDE_DIRECTORIES(. ${Boost_INCLUDE_DIR} ${SWIPL_INCLUDE_DIRS})
LINK_DIRECTORIES(${SWIPL_LIBRARY_DIRS})
# create the shared library
ADD_LIBRARY(imagine-planner SHARED ${sources})
# link necessary libraries
TARGET_LINK_LIBRARIES(imagine-planner ${SWIPL_LIBRARIES})
INSTALL(TARGETS imagine-planner
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib/iridrivers
......
......@@ -10,3 +10,10 @@ TARGET_LINK_LIBRARIES(types_test
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
ADD_EXECUTABLE(query_test query_test.cpp)
TARGET_LINK_LIBRARIES(query_test
imagine-planner
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
#include "types.h"
#include <iostream>
using namespace imagine_planner;
#if 0 // Set to 1 if __PRETTY_FUNCTION__ macro is not available
#define __PRETTY_FUNCTION__ __func__
#endif
#if 1
#define INFO(x)\
std::cout << "\e[1m\e[32m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl;
#else
#define INFO(x)
#endif
void test()
{
PlanState state{Predicate("at", "lettuce", "east"),
Predicate("at", "wolf", "east"),
Predicate("at", "bunny", "east"),
Predicate("at", "boat", "east"),
Predicate("side", "east"),
Predicate("side", "west"),
Predicate("forbidden", "wolf", "bunny"),
Predicate("forbidden", "lettuce", "bunny")};
PlCall("assertz((load_pre(Side,What):-at(boat,Side),at(What,Side),What\\==boat,side(Side),\\+loaded(_)))");
StateQuery query(state, create_term_v("Side", "What"), "load_pre");
//while (query.next_solution())
//{
//INFO("Side=" << query.get_values()[0] << ", What=" << query.get_values()[1]);
//}
}
int main(int argc, char* argv[])
{
PlEngine pl(argc, argv);
//PlTermv av(1);
//PlQuery q("current_module", av);
//while (q.next_solution())
//{
//std::cout << (char*)av[0] << std::endl;
//}
test();
}
......@@ -8,6 +8,10 @@
#include <unordered_set>
using namespace imagine_planner;
#if 0 // Set to 1 if __PRETTY_FUNCTION__ macro is not available
#define __PRETTY_FUNCTION__ __func__
#endif
#if 1
#define INFO(x)\
std::cout << "\e[1m\e[32m\e[4m" << __PRETTY_FUNCTION__ << " [" << __LINE__ << "]" << "\e[24m: " << x << "\e[0m" << std::endl;
......@@ -27,12 +31,6 @@ using namespace imagine_planner;
os << '}';\
return os;
AtomFactory A;
NumberFactory N;
VariableFactory V;
TermVFactory L;
PredicateFactory P;
const char* b2s(bool b)
{
return b? "true" : "false";
......@@ -43,218 +41,172 @@ std::ostream& operator<<(std::ostream& os, const std::set<Predicate>& ctn)
CONTAINER_TO_STREAM(ctn, os);
}
std::ostream& operator<<(std::ostream& os, const std::unordered_set<Predicate, std::hash<Term>>& ctn)
std::ostream& operator<<(std::ostream& os, const std::unordered_set<Predicate,
std::hash<Hashable>>& ctn)
{
CONTAINER_TO_STREAM(ctn, os);
}
BOOST_AUTO_TEST_CASE(atom_test)
BOOST_AUTO_TEST_CASE(literal_test)
{
auto atom1 = A("atom1");
auto atom1_ = A("atom1");
auto atom2 = A("atom2");
INFO("atom1=" << *atom1);
INFO("atom1_=" << *atom1_);
INFO("atom2=" << *atom2);
// Check name method
BOOST_CHECK_EQUAL(atom1->name(), "atom1");
// Check == operator
BOOST_CHECK_EQUAL(*atom1, *atom1_);
BOOST_CHECK_NE(*atom1, *atom2);
// Check < operator
BOOST_CHECK_LT(*atom1, *atom2);
BOOST_CHECK(not(*atom2 < *atom1));
// Check hash correctness
BOOST_CHECK_EQUAL(atom1->hash(), atom1_->hash());
BOOST_CHECK_NE(atom1->hash(), atom2->hash());
TermWrapper l1("atom1");
TermWrapper l1_("atom1");
TermWrapper l2("atom2");
TermWrapper l3("Var1");
TermWrapper l4("_");
INFO("l1=" << l1);
INFO("l1_=" << l1_);
INFO("l2=" << l2);
INFO("l3=" << l3);
BOOST_CHECK_EQUAL(l1, l1_);
BOOST_CHECK_LE(l1, l1_);
BOOST_CHECK_NE(l1, l2);
BOOST_CHECK_LT(l1, l2);
BOOST_CHECK_LE(l1, l2);
BOOST_CHECK_GT(l2, l1);
BOOST_CHECK_GE(l2, l1);
BOOST_CHECK(l1.is_ground());
BOOST_CHECK(l2.is_ground());
BOOST_CHECK(not l3.is_ground());
BOOST_CHECK(not l4.is_ground());
BOOST_CHECK_EQUAL(l1.hash(), l1_.hash());
BOOST_WARN_NE(l1.hash(), l2.hash()); // Very likely the hashes are different
}
BOOST_AUTO_TEST_CASE(number_test)
{
auto number1 = N(0.5);
auto number1_ = N(0.5+2e-10);
auto number1__ = N(0.5+5e-9);
auto number2 = N(1);
auto number2_ = N(1+2e-10);
auto number2__ = N(1+5e-9);
INFO("number1=" << *number1);
INFO("number1_=" << *number1_);
INFO("number1__=" << *number1__);
INFO("number2=" << *number2);
INFO("number2_=" << *number2_);
INFO("number2__=" << *number2__);
TermWrapper n1(0.5);
TermWrapper n1_(0.5+2e-10);
TermWrapper n1__(0.5+5e-9);
TermWrapper n2(1);
TermWrapper n2_(1+2e-10);
TermWrapper n2__(1+5e-9);
INFO("n1=" << n1);
INFO("n1_=" << n1_);
INFO("n1__=" << n1__);
INFO("n2=" << n2);
INFO("n2_=" << n2_);
INFO("n2__=" << n2__);
// Check number specific method's
BOOST_CHECK_CLOSE(number1->number(), 0.5, 0.00001);
BOOST_CHECK_CLOSE(number2->number(), 1, 0.00001);
BOOST_CHECK(not number1->is_int());
BOOST_CHECK(number2->is_int());
BOOST_CHECK(not number2__->is_int());
// Check == operator
BOOST_CHECK_EQUAL(*number1, *number1_);
BOOST_CHECK_EQUAL(*number2, *number2_);
BOOST_CHECK_NE(*number1, *number1__);
BOOST_CHECK_NE(*number2, *number2__);
// Check < operator
BOOST_CHECK_LT(*number1, *number2);
BOOST_CHECK_LT(*number1, *number1__);
BOOST_CHECK(not(*number1 < *number1_));
BOOST_CHECK(not(*number2 < *number1));
// Check hash correctness
BOOST_CHECK_EQUAL(number1->hash(), number1__->hash()); // non-ints should have the same hash
BOOST_CHECK_EQUAL(number2->hash(), number2_->hash());
BOOST_CHECK_NE(number2->hash(), number2__->hash());
}
BOOST_AUTO_TEST_CASE(variable_test)
{
auto variable1 = V("variable1");
auto variable1_ = V("variable1");
auto variable2 = V("variable2");
INFO("variable1=" << *variable1);
INFO("variable1_=" << *variable1_);
INFO("variable2=" << *variable2);
// Check name method
BOOST_CHECK_EQUAL(variable1->name(), "variable1");
BOOST_CHECK_CLOSE(n1.get_number_term()->get_number(), 0.5, 0.00001);
BOOST_CHECK_CLOSE(n2.get_number_term()->get_number(), 1, 0.00001);
BOOST_CHECK(not n1.get_number_term()->is_int());
BOOST_CHECK(not n2__.get_number_term()->is_int());
BOOST_CHECK(n2.get_number_term()->is_int());
BOOST_CHECK(n2_.get_number_term()->is_int());
// Check == operator
BOOST_CHECK_EQUAL(*variable1, *variable1_);
BOOST_CHECK_NE(*variable1, *variable2);
BOOST_CHECK_EQUAL(n1, n1_);
BOOST_CHECK_EQUAL(n2, n2_);
BOOST_CHECK_NE(n1, n1__);
BOOST_CHECK_NE(n2, n2__);
BOOST_CHECK_NE(n1, n2);
// Check < operator
BOOST_CHECK_LT(*variable1, *variable2);
BOOST_CHECK(not(*variable2 < *variable1));
BOOST_CHECK_LT(n1, n2);
BOOST_CHECK_LT(n1, n1__);
BOOST_CHECK_GE(n1_, n1);
BOOST_CHECK_GT(n2, n1);
// Check hash correctness
BOOST_CHECK_EQUAL(variable1->hash(), variable1_->hash());
BOOST_CHECK_NE(variable1->hash(), variable2->hash());
BOOST_CHECK_EQUAL(n1.hash(), n1.hash()); // non-ints should have the same hash
BOOST_CHECK_EQUAL(n2.hash(), n2_.hash());
BOOST_WARN_NE(n2.hash(), n2__.hash()); // Very likely the hashes are
// different
}
BOOST_AUTO_TEST_CASE(term_v_test)
BOOST_AUTO_TEST_CASE(mixed_test)
{
auto term_v_1 = L(A("a"), N(1), V("X"));
auto term_v_1_ = L(A("a"), N(1), V("X"));
auto term_v_2 = L(A("a"), N(1), A("x"));
auto term_v_3 = L(A("a"), A("x"));
auto term_v_4 = L(A("a"), A("y"));
auto term_v_5 = L(A("y"), A("a"));
INFO("term_v_1=" << *term_v_1);
INFO("term_v_1_=" << *term_v_1_);
INFO("term_v_2=" << *term_v_2);
INFO("term_v_3=" << *term_v_3);
INFO("term_v_4=" << *term_v_4);
INFO("term_v_5=" << *term_v_5);
// Check size, at, and iterators
BOOST_CHECK_EQUAL(term_v_1->size(), 3);
BOOST_CHECK_EQUAL(*term_v_1->at(0), *A("a"));
BOOST_CHECK_EQUAL(*term_v_1->at(1), *N(1));
BOOST_CHECK_EQUAL(*term_v_1->at(2), *V("X"));
auto it = term_v_1->begin();
BOOST_CHECK_EQUAL(**it, *A("a"));
it += 3;
BOOST_CHECK(it == term_v_1->end());
// Check is_ground
//BOOST_CHECK(term_v_2->is_ground());
//BOOST_CHECK(not ẗerm_v_1->is_ground());
// Check == operator
BOOST_CHECK_EQUAL(*term_v_1, *term_v_1_);
BOOST_CHECK_NE(*term_v_1, *term_v_2);
BOOST_CHECK_NE(*term_v_1, *term_v_3);
BOOST_CHECK_NE(*term_v_4, *term_v_5);
// Check < operator
BOOST_CHECK_LT(*term_v_2, *term_v_1);
BOOST_CHECK_LT(*term_v_3, *term_v_2);
BOOST_CHECK_LT(*term_v_3, *term_v_4);
// Check hash correctness
BOOST_CHECK_EQUAL(term_v_1->hash(), term_v_1_->hash());
BOOST_CHECK_NE(term_v_4->hash(), term_v_5->hash());
// Check to_str
BOOST_CHECK_EQUAL(term_v_4->to_str(), "a,y");
TermWrapper t1(0);
TermWrapper t2("atom");
INFO("t1=" << t1);
INFO("t2=" << t2);
BOOST_CHECK_NE(t1, t2);
BOOST_CHECK_LT(t2, t1);
BOOST_WARN_NE(t1.hash(), t2.hash());
}
BOOST_AUTO_TEST_CASE(predicate_test)
{
auto pred1 = P("p", A("x"), A("y"));
auto pred1_ = P("p", A("x"), A("y"));
auto pred2 = P("q", A("x"), A("y"));
auto pred3 = P("p", A("x"));
INFO("pred1=" << *pred1);
INFO("pred1_=" << *pred1_);
INFO("pred2=" << *pred2);
INFO("pred3=" << *pred3);
// Check == operator
BOOST_CHECK_EQUAL(*pred1, *pred1_);
BOOST_CHECK_NE(*pred1, *pred2);
BOOST_CHECK_NE(*pred1, *pred3);
// CHECK < OPERATOR
BOOST_CHECK_LT(*pred1, *pred2);
BOOST_CHECK_LT(*pred3, *pred1);
BOOST_CHECK(not(*pred1 < *pred3));
// Check to_str
BOOST_CHECK_EQUAL(pred1->to_str(), "p(x,y)");
Predicate p1("p", "x", "y");
Predicate p1_("p", "x", "y");
Predicate p1__("p", "y", "x");
Predicate p2("p", "X", "y");
Predicate p3("q", "x");
Predicate p4("q", 1);
Predicate p5("r");
INFO("p1=" << p1);
INFO("p1_=" << p1_);
INFO("p1__=" << p1__);
INFO("p2=" << p2);
INFO("p3=" << p3);
INFO("p4=" << p4);
INFO("p5=" << p5);
// Check ==, < operators
BOOST_CHECK_EQUAL(p1, p1_);
BOOST_CHECK_NE(p1, p1__);
BOOST_CHECK_NE(p1, p2);
BOOST_CHECK_NE(p1, p3);
BOOST_CHECK_NE(p1, p5);
BOOST_CHECK_LT(p1, p3);
BOOST_CHECK_LT(p3, p4);
// Hash correctness
BOOST_CHECK_EQUAL(p1.hash(), p1_.hash());
BOOST_WARN_NE(p1.hash(), p1__.hash());
BOOST_WARN_NE(p1.hash(), p5.hash());
// Ground?
BOOST_CHECK(p1.is_ground());
BOOST_CHECK(not p2.is_ground());
BOOST_CHECK(p5.is_ground());
// spec
BOOST_CHECK_EQUAL(p1.get_specification(), "p/2");
BOOST_CHECK_EQUAL(p1__.get_specification(), "p/2");
BOOST_CHECK_EQUAL(p3.get_specification(), "q/1");
BOOST_CHECK_EQUAL(p5.get_specification(), "r/0");
}
BOOST_AUTO_TEST_CASE(substitution_test)
template <class Set>
void set_test_aux()
{
Substitution sigma{{"X", A("a")}, {"Y", N(0)}};
INFO("sigma=" << sigma);
auto pred1 = P("p", V("X"), V("Y"));
auto pred2 = P("q", A("a"), V("Z"), V("X"));
auto pred1_ = sigma(pred1);
auto pred2_ = sigma(pred2);
BOOST_CHECK_EQUAL(*pred1_, *P("p", A("a"), N(0)));
BOOST_CHECK_EQUAL(*pred2_, *P("q", A("a"), V("Z"), A("a")));
// Further check the correctness of the < and == operators via set operations.
Predicate p1("p", "x", "y");
Predicate p1_("p", "x", "y");
Predicate p2("p", "X", 5);
Predicate p3("q", 0);
Predicate p4("r");
Set set{p1, p1_, p2, p3, p4};
INFO("Ordered set (on creation): " << set);
BOOST_CHECK_EQUAL(set.size(), 4);
set.erase(p1);
INFO("Ordered set (after removing p(x,y)): " << set);
BOOST_CHECK_EQUAL(set.size(), 3);
set.erase(Predicate("q", 5e-9));
INFO("Ordered set (after trying to remove non-existent number): " << set);
BOOST_CHECK_EQUAL(set.size(), 3);
set.erase(Predicate("q", 5e-10));
INFO("Ordered set (after removing existent number): " << set);
BOOST_CHECK_EQUAL(set.size(), 2);
set.insert(Predicate("r"));
INFO("Ordered set (after trying to insert existent predicate): " << set);
BOOST_CHECK_EQUAL(set.size(), 2);
set.insert(Predicate("s", 7));
INFO("Ordered set (after inserting non-existent predicate): " << set);
BOOST_CHECK_EQUAL(set.size(), 3);
}
BOOST_AUTO_TEST_CASE(ordered_set_test)
{
// Further check the correctness of the < and == operators via set operations.
Predicate p1("p", {A("x"), A("y")});
Predicate p1_("p", {A("x"), A("y")});
Predicate p2("p", {V("X"), N(5)});
Predicate p3("q", {N(0)});
Predicate p4("r", {});
std::set<Predicate> o_set{p1, p1_, p2, p3, p4};
INFO("Ordered set (on creation): " << o_set);
BOOST_CHECK_EQUAL(o_set.size(), 4);
o_set.erase(p1);
INFO("Ordered set (after removing p(x,y)): " << o_set);
BOOST_CHECK_EQUAL(o_set.size(), 3);
o_set.erase(Predicate("q", {N(5e-9)}));
INFO("Ordered set (after trying to remove non-existent number): " << o_set);
BOOST_CHECK_EQUAL(o_set.size(), 3);
o_set.erase(Predicate("q", {N(5e-10)}));
INFO("Ordered set (after removing existent number): " << o_set);
BOOST_CHECK_EQUAL(o_set.size(), 2);
o_set.insert(Predicate("r", {}));
INFO("Ordered set (after trying to insert existent predicate): " << o_set);
BOOST_CHECK_EQUAL(o_set.size(), 2);
o_set.insert(Predicate("s", {N(7)}));
INFO("Ordered set (after inserting non-existent predicate): " << o_set);
BOOST_CHECK_EQUAL(o_set.size(), 3);
typedef std::set<Predicate,std::less<Predicate>,std::allocator<Predicate>>
Set;
set_test_aux<Set>();
}
BOOST_AUTO_TEST_CASE(unordered_set_test)
{
// Further check the correctness of the hash and == methods via set operations.
Predicate p1("p", {A("x"), A("y")});
Predicate p1_("p", {A("x"), A("y")});
Predicate p2("p", {V("X"), N(5)});
Predicate p3("q", {N(0)});
Predicate p4("r", {});
std::unordered_set<Predicate, std::hash<Term>> u_set{p1, p1_, p2, p3, p4};
INFO("Unordered set (on creation): " << u_set);
BOOST_CHECK_EQUAL(u_set.size(), 4);
u_set.erase(p1);
INFO("Unordered set (after removing p(x,y)): " << u_set);
BOOST_CHECK_EQUAL(u_set.size(), 3);
u_set.erase(Predicate("q", {N(5e-9)}));
INFO("Unordered set (after trying to remove non-existent number): " << u_set);
BOOST_CHECK_EQUAL(u_set.size(), 3);
u_set.erase(Predicate("q", {N(5e-10)}));
INFO("Unordered set (after removing existent number): " << u_set);
BOOST_CHECK_EQUAL(u_set.size(), 2);
u_set.insert(Predicate("r", {}));
INFO("Unordered set (after trying to insert existent predicate): " << u_set);
BOOST_CHECK_EQUAL(u_set.size(), 2);
u_set.insert(Predicate("s", {N(7)}));
INFO("Unordered set (after inserting non-existent predicate): " << u_set);
BOOST_CHECK_EQUAL(u_set.size(), 3);
typedef std::unordered_set<Predicate,std::hash<Hashable>,
std::equal_to<Predicate>,std::allocator<Predicate>> Set;
set_test_aux<Set>();
}
BOOST_AUTO_TEST_CASE(state_query_test)
{
}
#include "types.h"
#include <iostream>
#include <cmath>
#include <sstream>
namespace imagine_planner
{
// Implementation of Atom's methods
// LiteralTerm
Atom::Atom(const std::string& name) : name_(name) {}
LiteralTerm::LiteralTerm(const std::string& literal) : literal_(literal) {}
bool Atom::operator==(const Term& other) const
std::size_t LiteralTerm::hash() const
{
if (const Atom* c_other = dynamic_cast<const Atom*>(&other))
{
return this->name_ == c_other->name_;
}
return false;
}
bool Atom::operator<(const Term& other) const
{
if (const Atom* c_other = dynamic_cast<const Atom*>(&other))
{
return this->name_ < c_other->name_;
}
return this->type() < other.type();
std::hash<std::string> h;
return h(literal_);
}
std::size_t Atom::hash() const
bool LiteralTerm::is_ground() const
{
std::hash<std::string> h;
return h(name_);
if (literal_.empty()) return false;
if (literal_[0] == '_') return false;
if (literal_[0] >= 'A' and literal_[0] <= 'Z') return false;
return true;
}
// Implementation of Number's methods
double Number::EPSILON_ = 1E-9;
Number::Number(double number) : number_(number) {}
bool Number::operator==(const Term& other) const
bool LiteralTerm::operator==(const Term& term) const
{
if (const Number* c_other = dynamic_cast<const Number*>(&other))
if (auto other = dynamic_cast<const LiteralTerm*>(&term))
{
return std::fabs(this->number_ - c_other->number_) < EPSILON_;
return this->literal_ == other->literal_;
}
return false;
}
bool Number::operator<(const Term& other) const
bool LiteralTerm::operator<(const Term& term) const
{
if (const Number* c_other = dynamic_cast<const Number*>(&other))
if (auto other = dynamic_cast<const LiteralTerm*>(&term))
{
return c_other->number_ - this->number_ >= EPSILON_;
return this->literal_ < other->literal_;
}
return this->type() < other.type();
return type() < term.type();
}
std::size_t Number::hash() const
// NumberTerm
double NumberTerm::EPSILON_ = 1E-9;
NumberTerm::NumberTerm(double number) : number_(number) {}
std::size_t NumberTerm::hash() const
{
std::hash<int> h;
// Return 0 for non-integer values to avoid problems in maps and sets
// (non-integer quantities would be assigned to the same bucket).
return is_int()? h(std::round(number_)) : 0;
return is_int()? h((int)std::round(number_)) : 0;
}
bool Number::is_int() const
bool NumberTerm::is_int() const
{
return std::fabs(number_ - std::round(number_)) < EPSILON_;
}
// Implementation of Variable's methods
Variable::Variable(const std::string& name) : name_(name) {}
bool Variable::operator==(const Term& other) const
bool NumberTerm::operator==(const Term& term) const
{
if (const Variable* c_other = dynamic_cast<const Variable*>(&other))
if (auto other = dynamic_cast<const NumberTerm*>(&term))
{
return this->name_ == c_other->name_;
return std::fabs(this->number_ - other->number_) < EPSILON_;
}
return false;
}
bool Variable::operator<(const Term& other) const
bool NumberTerm::operator<(const Term& term) const
{
if (const Variable* c_other = dynamic_cast<const Variable*>(&other))
if (auto other = dynamic_cast<const NumberTerm*>(&term))
{
return this->name_ < c_other->name_;
return other->number_ - this->number_ >= EPSILON_;
}
return this->type() < other.type();
}
std::size_t Variable::hash() const
{
std::hash<std::string> h;
return h(name_);
return type() < term.type();
}
<